diff options
Diffstat (limited to 'src')
76 files changed, 33279 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..9e34a8b --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,205 @@ +NULL = + +if ENABLE_SKEY +SUBDIRS = skey +endif + +bin_PROGRAMS = mate-terminal + +BUILT_SOURCES = \ + terminal-marshal.c \ + terminal-marshal.h \ + terminal-type-builtins.c \ + terminal-type-builtins.h \ + $(NULL) + +mate_terminal_SOURCES= \ + eggshell.c \ + eggshell.h \ + profile-editor.c \ + profile-editor.h \ + terminal.c \ + terminal-accels.c \ + terminal-accels.h \ + terminal-app.c \ + terminal-app.h \ + terminal-debug.c \ + terminal-debug.h \ + terminal-encoding.c \ + terminal-encoding.h \ + terminal-info-bar.c \ + terminal-info-bar.h \ + terminal-intl.h \ + terminal-options.c \ + terminal-options.h \ + terminal-profile.c \ + terminal-profile.h \ + terminal-screen.c \ + terminal-screen.h \ + terminal-screen-container.c \ + terminal-screen-container.h \ + terminal-search-dialog.c \ + terminal-search-dialog.h \ + terminal-tab-label.c \ + terminal-tab-label.h \ + terminal-tabs-menu.c \ + terminal-tabs-menu.h \ + terminal-util.c \ + terminal-util.h \ + terminal-version.h \ + terminal-window.c \ + terminal-window.h \ + $(NULL) + +if ENABLE_SKEY +mate_terminal_SOURCES += \ + skey-popup.c \ + skey-popup.h \ + $(NULL) +endif + +nodist_mate_terminal_SOURCES= $(BUILT_SOURCES) + +mate_terminal_CPPFLAGS = \ + -DTERMINAL_COMPILATION \ + -DEXECUTABLE_NAME=\"mate-terminal\" \ + -DTERM_DATADIR="\"$(datadir)\"" \ + -DTERM_LOCALEDIR="\"$(datadir)/locale\"" \ + -DTERM_PKGDATADIR="\"$(pkgdatadir)\"" \ + -DTERM_HELPDIR="\"$(HELP_DIR)\"" \ + -DSN_API_NOT_YET_FROZEN \ + -DGDK_MULTIHEAD_SAFE \ + -DG_DISABLE_SINGLE_INCLUDES \ + -DPANGO_DISABLE_SINGLE_INCLUDES \ + -DATK_DISABLE_SINGLE_INCLUDES \ + -DGDK_DISABLE_SINGLE_INCLUDES \ + -DGDK_PIXBUF_DISABLE_SINGLE_INCLUDES \ + -DGTK_DISABLE_SINGLE_INCLUDES \ + $(DISABLE_DEPRECATED) \ + $(AM_CPPFLAGS) + +mate_terminal_CFLAGS = \ + $(TERM_CFLAGS) \ + $(WARN_CFLAGS) \ + $(AM_CFLAGS) + +mate_terminal_LDFLAGS = + +mate_terminal_LDADD = \ + skey/libskey.la \ + $(TERM_LIBS) + +if WITH_SMCLIENT +mate_terminal_SOURCES += \ + eggsmclient.c \ + eggsmclient.h \ + eggsmclient-private.h \ + $(NULL) +mate_terminal_CFLAGS += $(SMCLIENT_CFLAGS) +mate_terminal_LDADD += $(SMCLIENT_LIBS) + +if WITH_SMCLIENT_XSMP +mate_terminal_SOURCES += \ + eggdesktopfile.c \ + eggdesktopfile.h \ + eggsmclient-xsmp.c \ + $(NULL) +mate_terminal_CPPFLAGS += -DEGG_SM_CLIENT_BACKEND_XSMP +endif +if WITH_SMCLIENT_WIN32 +mate_terminal_SOURCES += eggsmclient-win32.c +endif +if WITH_SMCLIENT_QUARTZ +mate_terminal_SOURCES += eggsmclient-osx.c +endif +endif + +TYPES_H_FILES = \ + terminal-profile.h \ + $(NULL) + +terminal-type-builtins.h: stamp-terminal-type-builtins.h + @true + +stamp-terminal-type-builtins.h: terminal-type-builtins.h.template $(TYPES_H_FILES) + $(AM_V_GEN) $(GLIB_MKENUMS) --template $< $(filter-out $<,$^) > xgen-ttbh \ + && (cmp -s xgen-ttbh terminal-type-builtins.h || cp xgen-ttbh terminal-type-builtins.h ) \ + && rm -f xgen-ttbh \ + && echo timestamp > $(@F) + +terminal-type-builtins.c: terminal-type-builtins.c.template $(TYPES_H_FILES) + $(AM_V_GEN) $(GLIB_MKENUMS) --template $< $(filter-out $<,$^) > xgen-ttbc \ + && (cmp -s xgen-ttbc terminal-type-builtins.c || cp xgen-ttbc terminal-type-builtins.c ) \ + && rm -f xgen-ttbc + +terminal-marshal.h: $(srcdir)/terminal-marshal.list + $(AM_V_GEN) ( $(GLIB_GENMARSHAL) --prefix=_terminal_marshal $(srcdir)/terminal-marshal.list \ + --header \ + --internal > terminal-marshal.h.tmp \ + && mv terminal-marshal.h.tmp terminal-marshal.h ) \ + || ( rm -f terminal-marshal.h.tmp && exit 1 ) + +terminal-marshal.c: $(srcdir)/terminal-marshal.list + $(AM_V_GEN) ( $(GLIB_GENMARSHAL) --prefix=_terminal_marshal $(srcdir)/terminal-marshal.list \ + --header \ + --body \ + --internal > terminal-marshal.c.tmp \ + && mv terminal-marshal.c.tmp terminal-marshal.c ) \ + || ( rm -f terminal-marshal.c.tmp && exit 1 ) + +schemadir = $(MATECONF_SCHEMA_FILE_DIR) +schema_in_files = mate-terminal.schemas.in +schema_DATA = mate-terminal.schemas + +aboutdir = $(pkgdatadir) +about_DATA = \ + terminal.about \ + $(NULL) + +uimanagerdir = $(pkgdatadir) +uimanager_DATA = \ + terminal.xml \ + $(NULL) + +builder_in_files = \ + encodings-dialog.glade \ + find-dialog.glade \ + keybinding-editor.glade \ + profile-manager.glade \ + profile-new-dialog.glade \ + profile-preferences.glade \ + skey-challenge.glade \ + $(NULL) + +builderdir = $(pkgdatadir) +builder_DATA = $(builder_in_files:.glade=.ui) + +CLEANFILES = \ + stamp-terminal-type-builtins.h \ + mate-terminal.schemas \ + stamp-terminal-type-builtins.h \ + $(builder_DATA) \ + $(BUILT_SOURCES) + +EXTRA_DIST = \ + terminal-marshal.list \ + terminal-type-builtins.c.template \ + terminal-type-builtins.h.template \ + extra-strings.c \ + $(about_DATA) \ + $(schema_in_files) \ + $(uimanager_DATA) \ + $(builder_in_files) \ + $(NULL) + +@INTLTOOL_SCHEMAS_RULE@ + +if MATECONF_SCHEMAS_INSTALL +install-data-local: + MATECONF_CONFIG_SOURCE=$(MATECONF_SCHEMA_CONFIG_SOURCE) $(MATECONFTOOL) --makefile-install-rule $(top_builddir)/src/$(schema_DATA) +endif + +%.ui: %.glade + $(AM_V_GEN) $(GTK_BUILDER_CONVERT) $< $@ + +-include $(top_srcdir)/git.mk diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..6007afc --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,1473 @@ +# 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-terminal$(EXEEXT) +@ENABLE_SKEY_TRUE@am__append_1 = \ +@ENABLE_SKEY_TRUE@ skey-popup.c \ +@ENABLE_SKEY_TRUE@ skey-popup.h \ +@ENABLE_SKEY_TRUE@ $(NULL) + +@WITH_SMCLIENT_TRUE@am__append_2 = \ +@WITH_SMCLIENT_TRUE@ eggsmclient.c \ +@WITH_SMCLIENT_TRUE@ eggsmclient.h \ +@WITH_SMCLIENT_TRUE@ eggsmclient-private.h \ +@WITH_SMCLIENT_TRUE@ $(NULL) + +@WITH_SMCLIENT_TRUE@am__append_3 = $(SMCLIENT_CFLAGS) +@WITH_SMCLIENT_TRUE@am__append_4 = $(SMCLIENT_LIBS) +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_XSMP_TRUE@am__append_5 = \ +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_XSMP_TRUE@ eggdesktopfile.c \ +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_XSMP_TRUE@ eggdesktopfile.h \ +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_XSMP_TRUE@ eggsmclient-xsmp.c \ +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_XSMP_TRUE@ $(NULL) + +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_XSMP_TRUE@am__append_6 = -DEGG_SM_CLIENT_BACKEND_XSMP +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_WIN32_TRUE@am__append_7 = eggsmclient-win32.c +@WITH_SMCLIENT_QUARTZ_TRUE@@WITH_SMCLIENT_TRUE@am__append_8 = eggsmclient-osx.c +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/terminal-version.h.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(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 = terminal-version.h +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(aboutdir)" \ + "$(DESTDIR)$(builderdir)" "$(DESTDIR)$(schemadir)" \ + "$(DESTDIR)$(uimanagerdir)" +PROGRAMS = $(bin_PROGRAMS) +am__mate_terminal_SOURCES_DIST = eggshell.c eggshell.h \ + profile-editor.c profile-editor.h terminal.c terminal-accels.c \ + terminal-accels.h terminal-app.c terminal-app.h \ + terminal-debug.c terminal-debug.h terminal-encoding.c \ + terminal-encoding.h terminal-info-bar.c terminal-info-bar.h \ + terminal-intl.h terminal-options.c terminal-options.h \ + terminal-profile.c terminal-profile.h terminal-screen.c \ + terminal-screen.h terminal-screen-container.c \ + terminal-screen-container.h terminal-search-dialog.c \ + terminal-search-dialog.h terminal-tab-label.c \ + terminal-tab-label.h terminal-tabs-menu.c terminal-tabs-menu.h \ + terminal-util.c terminal-util.h terminal-version.h \ + terminal-window.c terminal-window.h skey-popup.c skey-popup.h \ + eggsmclient.c eggsmclient.h eggsmclient-private.h \ + eggdesktopfile.c eggdesktopfile.h eggsmclient-xsmp.c \ + eggsmclient-win32.c eggsmclient-osx.c +am__objects_1 = +@ENABLE_SKEY_TRUE@am__objects_2 = mate_terminal-skey-popup.$(OBJEXT) \ +@ENABLE_SKEY_TRUE@ $(am__objects_1) +@WITH_SMCLIENT_TRUE@am__objects_3 = \ +@WITH_SMCLIENT_TRUE@ mate_terminal-eggsmclient.$(OBJEXT) \ +@WITH_SMCLIENT_TRUE@ $(am__objects_1) +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_XSMP_TRUE@am__objects_4 = mate_terminal-eggdesktopfile.$(OBJEXT) \ +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_XSMP_TRUE@ mate_terminal-eggsmclient-xsmp.$(OBJEXT) \ +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_XSMP_TRUE@ $(am__objects_1) +@WITH_SMCLIENT_TRUE@@WITH_SMCLIENT_WIN32_TRUE@am__objects_5 = mate_terminal-eggsmclient-win32.$(OBJEXT) +@WITH_SMCLIENT_QUARTZ_TRUE@@WITH_SMCLIENT_TRUE@am__objects_6 = mate_terminal-eggsmclient-osx.$(OBJEXT) +am_mate_terminal_OBJECTS = mate_terminal-eggshell.$(OBJEXT) \ + mate_terminal-profile-editor.$(OBJEXT) \ + mate_terminal-terminal.$(OBJEXT) \ + mate_terminal-terminal-accels.$(OBJEXT) \ + mate_terminal-terminal-app.$(OBJEXT) \ + mate_terminal-terminal-debug.$(OBJEXT) \ + mate_terminal-terminal-encoding.$(OBJEXT) \ + mate_terminal-terminal-info-bar.$(OBJEXT) \ + mate_terminal-terminal-options.$(OBJEXT) \ + mate_terminal-terminal-profile.$(OBJEXT) \ + mate_terminal-terminal-screen.$(OBJEXT) \ + mate_terminal-terminal-screen-container.$(OBJEXT) \ + mate_terminal-terminal-search-dialog.$(OBJEXT) \ + mate_terminal-terminal-tab-label.$(OBJEXT) \ + mate_terminal-terminal-tabs-menu.$(OBJEXT) \ + mate_terminal-terminal-util.$(OBJEXT) \ + mate_terminal-terminal-window.$(OBJEXT) $(am__objects_1) \ + $(am__objects_2) $(am__objects_3) $(am__objects_4) \ + $(am__objects_5) $(am__objects_6) +am__objects_7 = mate_terminal-terminal-marshal.$(OBJEXT) \ + mate_terminal-terminal-type-builtins.$(OBJEXT) \ + $(am__objects_1) +nodist_mate_terminal_OBJECTS = $(am__objects_7) +mate_terminal_OBJECTS = $(am_mate_terminal_OBJECTS) \ + $(nodist_mate_terminal_OBJECTS) +am__DEPENDENCIES_1 = +@WITH_SMCLIENT_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +mate_terminal_DEPENDENCIES = skey/libskey.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +mate_terminal_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mate_terminal_CFLAGS) \ + $(CFLAGS) $(mate_terminal_LDFLAGS) $(LDFLAGS) -o $@ +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_terminal_SOURCES) $(nodist_mate_terminal_SOURCES) +DIST_SOURCES = $(am__mate_terminal_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +DATA = $(about_DATA) $(builder_DATA) $(schema_DATA) $(uimanager_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = skey +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@ +DLLTOOL = @DLLTOOL@ +DOC_USER_FORMATS = @DOC_USER_FORMATS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +GTK_BUILDER_CONVERT = @GTK_BUILDER_CONVERT@ +HELP_DIR = @HELP_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +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_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@ +MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OMF_DIR = @OMF_DIR@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SMCLIENT_CFLAGS = @SMCLIENT_CFLAGS@ +SMCLIENT_LIBS = @SMCLIENT_LIBS@ +STRIP = @STRIP@ +TERMINAL_API_VERSION = @TERMINAL_API_VERSION@ +TERMINAL_MAJOR_VERSION = @TERMINAL_MAJOR_VERSION@ +TERMINAL_MICRO_VERSION = @TERMINAL_MICRO_VERSION@ +TERMINAL_MINOR_VERSION = @TERMINAL_MINOR_VERSION@ +TERM_CFLAGS = @TERM_CFLAGS@ +TERM_LIBS = @TERM_LIBS@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +XGETTEXT = @XGETTEXT@ +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@ +NULL = +@ENABLE_SKEY_TRUE@SUBDIRS = skey +BUILT_SOURCES = \ + terminal-marshal.c \ + terminal-marshal.h \ + terminal-type-builtins.c \ + terminal-type-builtins.h \ + $(NULL) + +mate_terminal_SOURCES = eggshell.c eggshell.h profile-editor.c \ + profile-editor.h terminal.c terminal-accels.c \ + terminal-accels.h terminal-app.c terminal-app.h \ + terminal-debug.c terminal-debug.h terminal-encoding.c \ + terminal-encoding.h terminal-info-bar.c terminal-info-bar.h \ + terminal-intl.h terminal-options.c terminal-options.h \ + terminal-profile.c terminal-profile.h terminal-screen.c \ + terminal-screen.h terminal-screen-container.c \ + terminal-screen-container.h terminal-search-dialog.c \ + terminal-search-dialog.h terminal-tab-label.c \ + terminal-tab-label.h terminal-tabs-menu.c terminal-tabs-menu.h \ + terminal-util.c terminal-util.h terminal-version.h \ + terminal-window.c terminal-window.h $(NULL) $(am__append_1) \ + $(am__append_2) $(am__append_5) $(am__append_7) \ + $(am__append_8) +nodist_mate_terminal_SOURCES = $(BUILT_SOURCES) +mate_terminal_CPPFLAGS = -DTERMINAL_COMPILATION \ + -DEXECUTABLE_NAME=\"mate-terminal\" \ + -DTERM_DATADIR="\"$(datadir)\"" \ + -DTERM_LOCALEDIR="\"$(datadir)/locale\"" \ + -DTERM_PKGDATADIR="\"$(pkgdatadir)\"" \ + -DTERM_HELPDIR="\"$(HELP_DIR)\"" -DSN_API_NOT_YET_FROZEN \ + -DGDK_MULTIHEAD_SAFE -DG_DISABLE_SINGLE_INCLUDES \ + -DPANGO_DISABLE_SINGLE_INCLUDES -DATK_DISABLE_SINGLE_INCLUDES \ + -DGDK_DISABLE_SINGLE_INCLUDES \ + -DGDK_PIXBUF_DISABLE_SINGLE_INCLUDES \ + -DGTK_DISABLE_SINGLE_INCLUDES $(DISABLE_DEPRECATED) \ + $(AM_CPPFLAGS) $(am__append_6) +mate_terminal_CFLAGS = $(TERM_CFLAGS) $(WARN_CFLAGS) $(AM_CFLAGS) \ + $(am__append_3) +mate_terminal_LDFLAGS = +mate_terminal_LDADD = skey/libskey.la $(TERM_LIBS) $(am__append_4) +TYPES_H_FILES = \ + terminal-profile.h \ + $(NULL) + +schemadir = $(MATECONF_SCHEMA_FILE_DIR) +schema_in_files = mate-terminal.schemas.in +schema_DATA = mate-terminal.schemas +aboutdir = $(pkgdatadir) +about_DATA = \ + terminal.about \ + $(NULL) + +uimanagerdir = $(pkgdatadir) +uimanager_DATA = \ + terminal.xml \ + $(NULL) + +builder_in_files = \ + encodings-dialog.glade \ + find-dialog.glade \ + keybinding-editor.glade \ + profile-manager.glade \ + profile-new-dialog.glade \ + profile-preferences.glade \ + skey-challenge.glade \ + $(NULL) + +builderdir = $(pkgdatadir) +builder_DATA = $(builder_in_files:.glade=.ui) +CLEANFILES = \ + stamp-terminal-type-builtins.h \ + mate-terminal.schemas \ + stamp-terminal-type-builtins.h \ + $(builder_DATA) \ + $(BUILT_SOURCES) + +EXTRA_DIST = \ + terminal-marshal.list \ + terminal-type-builtins.c.template \ + terminal-type-builtins.h.template \ + extra-strings.c \ + $(about_DATA) \ + $(schema_in_files) \ + $(uimanager_DATA) \ + $(builder_in_files) \ + $(NULL) + +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.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) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/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): +terminal-version.h: $(top_builddir)/config.status $(srcdir)/terminal-version.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +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-terminal$(EXEEXT): $(mate_terminal_OBJECTS) $(mate_terminal_DEPENDENCIES) + @rm -f mate-terminal$(EXEEXT) + $(AM_V_CCLD)$(mate_terminal_LINK) $(mate_terminal_OBJECTS) $(mate_terminal_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-eggdesktopfile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-eggshell.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-eggsmclient-osx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-eggsmclient-win32.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-eggsmclient-xsmp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-eggsmclient.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-profile-editor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-skey-popup.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-accels.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-app.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-debug.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-encoding.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-info-bar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-marshal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-options.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-profile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-screen-container.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-screen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-search-dialog.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-tab-label.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-tabs-menu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-type-builtins.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-util.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal-window.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mate_terminal-terminal.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 $@ $< + +mate_terminal-eggshell.o: eggshell.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggshell.o -MD -MP -MF $(DEPDIR)/mate_terminal-eggshell.Tpo -c -o mate_terminal-eggshell.o `test -f 'eggshell.c' || echo '$(srcdir)/'`eggshell.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggshell.Tpo $(DEPDIR)/mate_terminal-eggshell.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggshell.c' object='mate_terminal-eggshell.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggshell.o `test -f 'eggshell.c' || echo '$(srcdir)/'`eggshell.c + +mate_terminal-eggshell.obj: eggshell.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggshell.obj -MD -MP -MF $(DEPDIR)/mate_terminal-eggshell.Tpo -c -o mate_terminal-eggshell.obj `if test -f 'eggshell.c'; then $(CYGPATH_W) 'eggshell.c'; else $(CYGPATH_W) '$(srcdir)/eggshell.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggshell.Tpo $(DEPDIR)/mate_terminal-eggshell.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggshell.c' object='mate_terminal-eggshell.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggshell.obj `if test -f 'eggshell.c'; then $(CYGPATH_W) 'eggshell.c'; else $(CYGPATH_W) '$(srcdir)/eggshell.c'; fi` + +mate_terminal-profile-editor.o: profile-editor.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-profile-editor.o -MD -MP -MF $(DEPDIR)/mate_terminal-profile-editor.Tpo -c -o mate_terminal-profile-editor.o `test -f 'profile-editor.c' || echo '$(srcdir)/'`profile-editor.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-profile-editor.Tpo $(DEPDIR)/mate_terminal-profile-editor.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='profile-editor.c' object='mate_terminal-profile-editor.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-profile-editor.o `test -f 'profile-editor.c' || echo '$(srcdir)/'`profile-editor.c + +mate_terminal-profile-editor.obj: profile-editor.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-profile-editor.obj -MD -MP -MF $(DEPDIR)/mate_terminal-profile-editor.Tpo -c -o mate_terminal-profile-editor.obj `if test -f 'profile-editor.c'; then $(CYGPATH_W) 'profile-editor.c'; else $(CYGPATH_W) '$(srcdir)/profile-editor.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-profile-editor.Tpo $(DEPDIR)/mate_terminal-profile-editor.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='profile-editor.c' object='mate_terminal-profile-editor.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-profile-editor.obj `if test -f 'profile-editor.c'; then $(CYGPATH_W) 'profile-editor.c'; else $(CYGPATH_W) '$(srcdir)/profile-editor.c'; fi` + +mate_terminal-terminal.o: terminal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal.Tpo -c -o mate_terminal-terminal.o `test -f 'terminal.c' || echo '$(srcdir)/'`terminal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal.Tpo $(DEPDIR)/mate_terminal-terminal.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal.c' object='mate_terminal-terminal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal.o `test -f 'terminal.c' || echo '$(srcdir)/'`terminal.c + +mate_terminal-terminal.obj: terminal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal.Tpo -c -o mate_terminal-terminal.obj `if test -f 'terminal.c'; then $(CYGPATH_W) 'terminal.c'; else $(CYGPATH_W) '$(srcdir)/terminal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal.Tpo $(DEPDIR)/mate_terminal-terminal.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal.c' object='mate_terminal-terminal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal.obj `if test -f 'terminal.c'; then $(CYGPATH_W) 'terminal.c'; else $(CYGPATH_W) '$(srcdir)/terminal.c'; fi` + +mate_terminal-terminal-accels.o: terminal-accels.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-accels.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-accels.Tpo -c -o mate_terminal-terminal-accels.o `test -f 'terminal-accels.c' || echo '$(srcdir)/'`terminal-accels.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-accels.Tpo $(DEPDIR)/mate_terminal-terminal-accels.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-accels.c' object='mate_terminal-terminal-accels.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-accels.o `test -f 'terminal-accels.c' || echo '$(srcdir)/'`terminal-accels.c + +mate_terminal-terminal-accels.obj: terminal-accels.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-accels.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-accels.Tpo -c -o mate_terminal-terminal-accels.obj `if test -f 'terminal-accels.c'; then $(CYGPATH_W) 'terminal-accels.c'; else $(CYGPATH_W) '$(srcdir)/terminal-accels.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-accels.Tpo $(DEPDIR)/mate_terminal-terminal-accels.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-accels.c' object='mate_terminal-terminal-accels.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-accels.obj `if test -f 'terminal-accels.c'; then $(CYGPATH_W) 'terminal-accels.c'; else $(CYGPATH_W) '$(srcdir)/terminal-accels.c'; fi` + +mate_terminal-terminal-app.o: terminal-app.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-app.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-app.Tpo -c -o mate_terminal-terminal-app.o `test -f 'terminal-app.c' || echo '$(srcdir)/'`terminal-app.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-app.Tpo $(DEPDIR)/mate_terminal-terminal-app.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-app.c' object='mate_terminal-terminal-app.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-app.o `test -f 'terminal-app.c' || echo '$(srcdir)/'`terminal-app.c + +mate_terminal-terminal-app.obj: terminal-app.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-app.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-app.Tpo -c -o mate_terminal-terminal-app.obj `if test -f 'terminal-app.c'; then $(CYGPATH_W) 'terminal-app.c'; else $(CYGPATH_W) '$(srcdir)/terminal-app.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-app.Tpo $(DEPDIR)/mate_terminal-terminal-app.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-app.c' object='mate_terminal-terminal-app.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-app.obj `if test -f 'terminal-app.c'; then $(CYGPATH_W) 'terminal-app.c'; else $(CYGPATH_W) '$(srcdir)/terminal-app.c'; fi` + +mate_terminal-terminal-debug.o: terminal-debug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-debug.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-debug.Tpo -c -o mate_terminal-terminal-debug.o `test -f 'terminal-debug.c' || echo '$(srcdir)/'`terminal-debug.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-debug.Tpo $(DEPDIR)/mate_terminal-terminal-debug.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-debug.c' object='mate_terminal-terminal-debug.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-debug.o `test -f 'terminal-debug.c' || echo '$(srcdir)/'`terminal-debug.c + +mate_terminal-terminal-debug.obj: terminal-debug.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-debug.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-debug.Tpo -c -o mate_terminal-terminal-debug.obj `if test -f 'terminal-debug.c'; then $(CYGPATH_W) 'terminal-debug.c'; else $(CYGPATH_W) '$(srcdir)/terminal-debug.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-debug.Tpo $(DEPDIR)/mate_terminal-terminal-debug.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-debug.c' object='mate_terminal-terminal-debug.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-debug.obj `if test -f 'terminal-debug.c'; then $(CYGPATH_W) 'terminal-debug.c'; else $(CYGPATH_W) '$(srcdir)/terminal-debug.c'; fi` + +mate_terminal-terminal-encoding.o: terminal-encoding.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-encoding.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-encoding.Tpo -c -o mate_terminal-terminal-encoding.o `test -f 'terminal-encoding.c' || echo '$(srcdir)/'`terminal-encoding.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-encoding.Tpo $(DEPDIR)/mate_terminal-terminal-encoding.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-encoding.c' object='mate_terminal-terminal-encoding.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-encoding.o `test -f 'terminal-encoding.c' || echo '$(srcdir)/'`terminal-encoding.c + +mate_terminal-terminal-encoding.obj: terminal-encoding.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-encoding.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-encoding.Tpo -c -o mate_terminal-terminal-encoding.obj `if test -f 'terminal-encoding.c'; then $(CYGPATH_W) 'terminal-encoding.c'; else $(CYGPATH_W) '$(srcdir)/terminal-encoding.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-encoding.Tpo $(DEPDIR)/mate_terminal-terminal-encoding.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-encoding.c' object='mate_terminal-terminal-encoding.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-encoding.obj `if test -f 'terminal-encoding.c'; then $(CYGPATH_W) 'terminal-encoding.c'; else $(CYGPATH_W) '$(srcdir)/terminal-encoding.c'; fi` + +mate_terminal-terminal-info-bar.o: terminal-info-bar.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-info-bar.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-info-bar.Tpo -c -o mate_terminal-terminal-info-bar.o `test -f 'terminal-info-bar.c' || echo '$(srcdir)/'`terminal-info-bar.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-info-bar.Tpo $(DEPDIR)/mate_terminal-terminal-info-bar.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-info-bar.c' object='mate_terminal-terminal-info-bar.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-info-bar.o `test -f 'terminal-info-bar.c' || echo '$(srcdir)/'`terminal-info-bar.c + +mate_terminal-terminal-info-bar.obj: terminal-info-bar.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-info-bar.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-info-bar.Tpo -c -o mate_terminal-terminal-info-bar.obj `if test -f 'terminal-info-bar.c'; then $(CYGPATH_W) 'terminal-info-bar.c'; else $(CYGPATH_W) '$(srcdir)/terminal-info-bar.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-info-bar.Tpo $(DEPDIR)/mate_terminal-terminal-info-bar.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-info-bar.c' object='mate_terminal-terminal-info-bar.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-info-bar.obj `if test -f 'terminal-info-bar.c'; then $(CYGPATH_W) 'terminal-info-bar.c'; else $(CYGPATH_W) '$(srcdir)/terminal-info-bar.c'; fi` + +mate_terminal-terminal-options.o: terminal-options.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-options.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-options.Tpo -c -o mate_terminal-terminal-options.o `test -f 'terminal-options.c' || echo '$(srcdir)/'`terminal-options.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-options.Tpo $(DEPDIR)/mate_terminal-terminal-options.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-options.c' object='mate_terminal-terminal-options.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-options.o `test -f 'terminal-options.c' || echo '$(srcdir)/'`terminal-options.c + +mate_terminal-terminal-options.obj: terminal-options.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-options.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-options.Tpo -c -o mate_terminal-terminal-options.obj `if test -f 'terminal-options.c'; then $(CYGPATH_W) 'terminal-options.c'; else $(CYGPATH_W) '$(srcdir)/terminal-options.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-options.Tpo $(DEPDIR)/mate_terminal-terminal-options.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-options.c' object='mate_terminal-terminal-options.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-options.obj `if test -f 'terminal-options.c'; then $(CYGPATH_W) 'terminal-options.c'; else $(CYGPATH_W) '$(srcdir)/terminal-options.c'; fi` + +mate_terminal-terminal-profile.o: terminal-profile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-profile.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-profile.Tpo -c -o mate_terminal-terminal-profile.o `test -f 'terminal-profile.c' || echo '$(srcdir)/'`terminal-profile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-profile.Tpo $(DEPDIR)/mate_terminal-terminal-profile.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-profile.c' object='mate_terminal-terminal-profile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-profile.o `test -f 'terminal-profile.c' || echo '$(srcdir)/'`terminal-profile.c + +mate_terminal-terminal-profile.obj: terminal-profile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-profile.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-profile.Tpo -c -o mate_terminal-terminal-profile.obj `if test -f 'terminal-profile.c'; then $(CYGPATH_W) 'terminal-profile.c'; else $(CYGPATH_W) '$(srcdir)/terminal-profile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-profile.Tpo $(DEPDIR)/mate_terminal-terminal-profile.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-profile.c' object='mate_terminal-terminal-profile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-profile.obj `if test -f 'terminal-profile.c'; then $(CYGPATH_W) 'terminal-profile.c'; else $(CYGPATH_W) '$(srcdir)/terminal-profile.c'; fi` + +mate_terminal-terminal-screen.o: terminal-screen.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-screen.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-screen.Tpo -c -o mate_terminal-terminal-screen.o `test -f 'terminal-screen.c' || echo '$(srcdir)/'`terminal-screen.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-screen.Tpo $(DEPDIR)/mate_terminal-terminal-screen.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-screen.c' object='mate_terminal-terminal-screen.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-screen.o `test -f 'terminal-screen.c' || echo '$(srcdir)/'`terminal-screen.c + +mate_terminal-terminal-screen.obj: terminal-screen.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-screen.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-screen.Tpo -c -o mate_terminal-terminal-screen.obj `if test -f 'terminal-screen.c'; then $(CYGPATH_W) 'terminal-screen.c'; else $(CYGPATH_W) '$(srcdir)/terminal-screen.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-screen.Tpo $(DEPDIR)/mate_terminal-terminal-screen.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-screen.c' object='mate_terminal-terminal-screen.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-screen.obj `if test -f 'terminal-screen.c'; then $(CYGPATH_W) 'terminal-screen.c'; else $(CYGPATH_W) '$(srcdir)/terminal-screen.c'; fi` + +mate_terminal-terminal-screen-container.o: terminal-screen-container.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-screen-container.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-screen-container.Tpo -c -o mate_terminal-terminal-screen-container.o `test -f 'terminal-screen-container.c' || echo '$(srcdir)/'`terminal-screen-container.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-screen-container.Tpo $(DEPDIR)/mate_terminal-terminal-screen-container.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-screen-container.c' object='mate_terminal-terminal-screen-container.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-screen-container.o `test -f 'terminal-screen-container.c' || echo '$(srcdir)/'`terminal-screen-container.c + +mate_terminal-terminal-screen-container.obj: terminal-screen-container.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-screen-container.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-screen-container.Tpo -c -o mate_terminal-terminal-screen-container.obj `if test -f 'terminal-screen-container.c'; then $(CYGPATH_W) 'terminal-screen-container.c'; else $(CYGPATH_W) '$(srcdir)/terminal-screen-container.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-screen-container.Tpo $(DEPDIR)/mate_terminal-terminal-screen-container.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-screen-container.c' object='mate_terminal-terminal-screen-container.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-screen-container.obj `if test -f 'terminal-screen-container.c'; then $(CYGPATH_W) 'terminal-screen-container.c'; else $(CYGPATH_W) '$(srcdir)/terminal-screen-container.c'; fi` + +mate_terminal-terminal-search-dialog.o: terminal-search-dialog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-search-dialog.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-search-dialog.Tpo -c -o mate_terminal-terminal-search-dialog.o `test -f 'terminal-search-dialog.c' || echo '$(srcdir)/'`terminal-search-dialog.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-search-dialog.Tpo $(DEPDIR)/mate_terminal-terminal-search-dialog.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-search-dialog.c' object='mate_terminal-terminal-search-dialog.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-search-dialog.o `test -f 'terminal-search-dialog.c' || echo '$(srcdir)/'`terminal-search-dialog.c + +mate_terminal-terminal-search-dialog.obj: terminal-search-dialog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-search-dialog.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-search-dialog.Tpo -c -o mate_terminal-terminal-search-dialog.obj `if test -f 'terminal-search-dialog.c'; then $(CYGPATH_W) 'terminal-search-dialog.c'; else $(CYGPATH_W) '$(srcdir)/terminal-search-dialog.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-search-dialog.Tpo $(DEPDIR)/mate_terminal-terminal-search-dialog.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-search-dialog.c' object='mate_terminal-terminal-search-dialog.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-search-dialog.obj `if test -f 'terminal-search-dialog.c'; then $(CYGPATH_W) 'terminal-search-dialog.c'; else $(CYGPATH_W) '$(srcdir)/terminal-search-dialog.c'; fi` + +mate_terminal-terminal-tab-label.o: terminal-tab-label.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-tab-label.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-tab-label.Tpo -c -o mate_terminal-terminal-tab-label.o `test -f 'terminal-tab-label.c' || echo '$(srcdir)/'`terminal-tab-label.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-tab-label.Tpo $(DEPDIR)/mate_terminal-terminal-tab-label.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-tab-label.c' object='mate_terminal-terminal-tab-label.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-tab-label.o `test -f 'terminal-tab-label.c' || echo '$(srcdir)/'`terminal-tab-label.c + +mate_terminal-terminal-tab-label.obj: terminal-tab-label.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-tab-label.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-tab-label.Tpo -c -o mate_terminal-terminal-tab-label.obj `if test -f 'terminal-tab-label.c'; then $(CYGPATH_W) 'terminal-tab-label.c'; else $(CYGPATH_W) '$(srcdir)/terminal-tab-label.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-tab-label.Tpo $(DEPDIR)/mate_terminal-terminal-tab-label.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-tab-label.c' object='mate_terminal-terminal-tab-label.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-tab-label.obj `if test -f 'terminal-tab-label.c'; then $(CYGPATH_W) 'terminal-tab-label.c'; else $(CYGPATH_W) '$(srcdir)/terminal-tab-label.c'; fi` + +mate_terminal-terminal-tabs-menu.o: terminal-tabs-menu.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-tabs-menu.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-tabs-menu.Tpo -c -o mate_terminal-terminal-tabs-menu.o `test -f 'terminal-tabs-menu.c' || echo '$(srcdir)/'`terminal-tabs-menu.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-tabs-menu.Tpo $(DEPDIR)/mate_terminal-terminal-tabs-menu.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-tabs-menu.c' object='mate_terminal-terminal-tabs-menu.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-tabs-menu.o `test -f 'terminal-tabs-menu.c' || echo '$(srcdir)/'`terminal-tabs-menu.c + +mate_terminal-terminal-tabs-menu.obj: terminal-tabs-menu.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-tabs-menu.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-tabs-menu.Tpo -c -o mate_terminal-terminal-tabs-menu.obj `if test -f 'terminal-tabs-menu.c'; then $(CYGPATH_W) 'terminal-tabs-menu.c'; else $(CYGPATH_W) '$(srcdir)/terminal-tabs-menu.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-tabs-menu.Tpo $(DEPDIR)/mate_terminal-terminal-tabs-menu.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-tabs-menu.c' object='mate_terminal-terminal-tabs-menu.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-tabs-menu.obj `if test -f 'terminal-tabs-menu.c'; then $(CYGPATH_W) 'terminal-tabs-menu.c'; else $(CYGPATH_W) '$(srcdir)/terminal-tabs-menu.c'; fi` + +mate_terminal-terminal-util.o: terminal-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-util.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-util.Tpo -c -o mate_terminal-terminal-util.o `test -f 'terminal-util.c' || echo '$(srcdir)/'`terminal-util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-util.Tpo $(DEPDIR)/mate_terminal-terminal-util.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-util.c' object='mate_terminal-terminal-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-util.o `test -f 'terminal-util.c' || echo '$(srcdir)/'`terminal-util.c + +mate_terminal-terminal-util.obj: terminal-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-util.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-util.Tpo -c -o mate_terminal-terminal-util.obj `if test -f 'terminal-util.c'; then $(CYGPATH_W) 'terminal-util.c'; else $(CYGPATH_W) '$(srcdir)/terminal-util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-util.Tpo $(DEPDIR)/mate_terminal-terminal-util.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-util.c' object='mate_terminal-terminal-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-util.obj `if test -f 'terminal-util.c'; then $(CYGPATH_W) 'terminal-util.c'; else $(CYGPATH_W) '$(srcdir)/terminal-util.c'; fi` + +mate_terminal-terminal-window.o: terminal-window.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-window.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-window.Tpo -c -o mate_terminal-terminal-window.o `test -f 'terminal-window.c' || echo '$(srcdir)/'`terminal-window.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-window.Tpo $(DEPDIR)/mate_terminal-terminal-window.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-window.c' object='mate_terminal-terminal-window.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-window.o `test -f 'terminal-window.c' || echo '$(srcdir)/'`terminal-window.c + +mate_terminal-terminal-window.obj: terminal-window.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-window.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-window.Tpo -c -o mate_terminal-terminal-window.obj `if test -f 'terminal-window.c'; then $(CYGPATH_W) 'terminal-window.c'; else $(CYGPATH_W) '$(srcdir)/terminal-window.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-window.Tpo $(DEPDIR)/mate_terminal-terminal-window.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-window.c' object='mate_terminal-terminal-window.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-window.obj `if test -f 'terminal-window.c'; then $(CYGPATH_W) 'terminal-window.c'; else $(CYGPATH_W) '$(srcdir)/terminal-window.c'; fi` + +mate_terminal-skey-popup.o: skey-popup.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-skey-popup.o -MD -MP -MF $(DEPDIR)/mate_terminal-skey-popup.Tpo -c -o mate_terminal-skey-popup.o `test -f 'skey-popup.c' || echo '$(srcdir)/'`skey-popup.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-skey-popup.Tpo $(DEPDIR)/mate_terminal-skey-popup.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='skey-popup.c' object='mate_terminal-skey-popup.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-skey-popup.o `test -f 'skey-popup.c' || echo '$(srcdir)/'`skey-popup.c + +mate_terminal-skey-popup.obj: skey-popup.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-skey-popup.obj -MD -MP -MF $(DEPDIR)/mate_terminal-skey-popup.Tpo -c -o mate_terminal-skey-popup.obj `if test -f 'skey-popup.c'; then $(CYGPATH_W) 'skey-popup.c'; else $(CYGPATH_W) '$(srcdir)/skey-popup.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-skey-popup.Tpo $(DEPDIR)/mate_terminal-skey-popup.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='skey-popup.c' object='mate_terminal-skey-popup.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-skey-popup.obj `if test -f 'skey-popup.c'; then $(CYGPATH_W) 'skey-popup.c'; else $(CYGPATH_W) '$(srcdir)/skey-popup.c'; fi` + +mate_terminal-eggsmclient.o: eggsmclient.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggsmclient.o -MD -MP -MF $(DEPDIR)/mate_terminal-eggsmclient.Tpo -c -o mate_terminal-eggsmclient.o `test -f 'eggsmclient.c' || echo '$(srcdir)/'`eggsmclient.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggsmclient.Tpo $(DEPDIR)/mate_terminal-eggsmclient.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggsmclient.c' object='mate_terminal-eggsmclient.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggsmclient.o `test -f 'eggsmclient.c' || echo '$(srcdir)/'`eggsmclient.c + +mate_terminal-eggsmclient.obj: eggsmclient.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggsmclient.obj -MD -MP -MF $(DEPDIR)/mate_terminal-eggsmclient.Tpo -c -o mate_terminal-eggsmclient.obj `if test -f 'eggsmclient.c'; then $(CYGPATH_W) 'eggsmclient.c'; else $(CYGPATH_W) '$(srcdir)/eggsmclient.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggsmclient.Tpo $(DEPDIR)/mate_terminal-eggsmclient.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggsmclient.c' object='mate_terminal-eggsmclient.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggsmclient.obj `if test -f 'eggsmclient.c'; then $(CYGPATH_W) 'eggsmclient.c'; else $(CYGPATH_W) '$(srcdir)/eggsmclient.c'; fi` + +mate_terminal-eggdesktopfile.o: eggdesktopfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggdesktopfile.o -MD -MP -MF $(DEPDIR)/mate_terminal-eggdesktopfile.Tpo -c -o mate_terminal-eggdesktopfile.o `test -f 'eggdesktopfile.c' || echo '$(srcdir)/'`eggdesktopfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggdesktopfile.Tpo $(DEPDIR)/mate_terminal-eggdesktopfile.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggdesktopfile.c' object='mate_terminal-eggdesktopfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggdesktopfile.o `test -f 'eggdesktopfile.c' || echo '$(srcdir)/'`eggdesktopfile.c + +mate_terminal-eggdesktopfile.obj: eggdesktopfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggdesktopfile.obj -MD -MP -MF $(DEPDIR)/mate_terminal-eggdesktopfile.Tpo -c -o mate_terminal-eggdesktopfile.obj `if test -f 'eggdesktopfile.c'; then $(CYGPATH_W) 'eggdesktopfile.c'; else $(CYGPATH_W) '$(srcdir)/eggdesktopfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggdesktopfile.Tpo $(DEPDIR)/mate_terminal-eggdesktopfile.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggdesktopfile.c' object='mate_terminal-eggdesktopfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggdesktopfile.obj `if test -f 'eggdesktopfile.c'; then $(CYGPATH_W) 'eggdesktopfile.c'; else $(CYGPATH_W) '$(srcdir)/eggdesktopfile.c'; fi` + +mate_terminal-eggsmclient-xsmp.o: eggsmclient-xsmp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggsmclient-xsmp.o -MD -MP -MF $(DEPDIR)/mate_terminal-eggsmclient-xsmp.Tpo -c -o mate_terminal-eggsmclient-xsmp.o `test -f 'eggsmclient-xsmp.c' || echo '$(srcdir)/'`eggsmclient-xsmp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggsmclient-xsmp.Tpo $(DEPDIR)/mate_terminal-eggsmclient-xsmp.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggsmclient-xsmp.c' object='mate_terminal-eggsmclient-xsmp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggsmclient-xsmp.o `test -f 'eggsmclient-xsmp.c' || echo '$(srcdir)/'`eggsmclient-xsmp.c + +mate_terminal-eggsmclient-xsmp.obj: eggsmclient-xsmp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggsmclient-xsmp.obj -MD -MP -MF $(DEPDIR)/mate_terminal-eggsmclient-xsmp.Tpo -c -o mate_terminal-eggsmclient-xsmp.obj `if test -f 'eggsmclient-xsmp.c'; then $(CYGPATH_W) 'eggsmclient-xsmp.c'; else $(CYGPATH_W) '$(srcdir)/eggsmclient-xsmp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggsmclient-xsmp.Tpo $(DEPDIR)/mate_terminal-eggsmclient-xsmp.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggsmclient-xsmp.c' object='mate_terminal-eggsmclient-xsmp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggsmclient-xsmp.obj `if test -f 'eggsmclient-xsmp.c'; then $(CYGPATH_W) 'eggsmclient-xsmp.c'; else $(CYGPATH_W) '$(srcdir)/eggsmclient-xsmp.c'; fi` + +mate_terminal-eggsmclient-win32.o: eggsmclient-win32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggsmclient-win32.o -MD -MP -MF $(DEPDIR)/mate_terminal-eggsmclient-win32.Tpo -c -o mate_terminal-eggsmclient-win32.o `test -f 'eggsmclient-win32.c' || echo '$(srcdir)/'`eggsmclient-win32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggsmclient-win32.Tpo $(DEPDIR)/mate_terminal-eggsmclient-win32.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggsmclient-win32.c' object='mate_terminal-eggsmclient-win32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggsmclient-win32.o `test -f 'eggsmclient-win32.c' || echo '$(srcdir)/'`eggsmclient-win32.c + +mate_terminal-eggsmclient-win32.obj: eggsmclient-win32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggsmclient-win32.obj -MD -MP -MF $(DEPDIR)/mate_terminal-eggsmclient-win32.Tpo -c -o mate_terminal-eggsmclient-win32.obj `if test -f 'eggsmclient-win32.c'; then $(CYGPATH_W) 'eggsmclient-win32.c'; else $(CYGPATH_W) '$(srcdir)/eggsmclient-win32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggsmclient-win32.Tpo $(DEPDIR)/mate_terminal-eggsmclient-win32.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggsmclient-win32.c' object='mate_terminal-eggsmclient-win32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggsmclient-win32.obj `if test -f 'eggsmclient-win32.c'; then $(CYGPATH_W) 'eggsmclient-win32.c'; else $(CYGPATH_W) '$(srcdir)/eggsmclient-win32.c'; fi` + +mate_terminal-eggsmclient-osx.o: eggsmclient-osx.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggsmclient-osx.o -MD -MP -MF $(DEPDIR)/mate_terminal-eggsmclient-osx.Tpo -c -o mate_terminal-eggsmclient-osx.o `test -f 'eggsmclient-osx.c' || echo '$(srcdir)/'`eggsmclient-osx.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggsmclient-osx.Tpo $(DEPDIR)/mate_terminal-eggsmclient-osx.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggsmclient-osx.c' object='mate_terminal-eggsmclient-osx.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggsmclient-osx.o `test -f 'eggsmclient-osx.c' || echo '$(srcdir)/'`eggsmclient-osx.c + +mate_terminal-eggsmclient-osx.obj: eggsmclient-osx.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-eggsmclient-osx.obj -MD -MP -MF $(DEPDIR)/mate_terminal-eggsmclient-osx.Tpo -c -o mate_terminal-eggsmclient-osx.obj `if test -f 'eggsmclient-osx.c'; then $(CYGPATH_W) 'eggsmclient-osx.c'; else $(CYGPATH_W) '$(srcdir)/eggsmclient-osx.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-eggsmclient-osx.Tpo $(DEPDIR)/mate_terminal-eggsmclient-osx.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eggsmclient-osx.c' object='mate_terminal-eggsmclient-osx.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-eggsmclient-osx.obj `if test -f 'eggsmclient-osx.c'; then $(CYGPATH_W) 'eggsmclient-osx.c'; else $(CYGPATH_W) '$(srcdir)/eggsmclient-osx.c'; fi` + +mate_terminal-terminal-marshal.o: terminal-marshal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-marshal.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-marshal.Tpo -c -o mate_terminal-terminal-marshal.o `test -f 'terminal-marshal.c' || echo '$(srcdir)/'`terminal-marshal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-marshal.Tpo $(DEPDIR)/mate_terminal-terminal-marshal.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-marshal.c' object='mate_terminal-terminal-marshal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-marshal.o `test -f 'terminal-marshal.c' || echo '$(srcdir)/'`terminal-marshal.c + +mate_terminal-terminal-marshal.obj: terminal-marshal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-marshal.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-marshal.Tpo -c -o mate_terminal-terminal-marshal.obj `if test -f 'terminal-marshal.c'; then $(CYGPATH_W) 'terminal-marshal.c'; else $(CYGPATH_W) '$(srcdir)/terminal-marshal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-marshal.Tpo $(DEPDIR)/mate_terminal-terminal-marshal.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-marshal.c' object='mate_terminal-terminal-marshal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-marshal.obj `if test -f 'terminal-marshal.c'; then $(CYGPATH_W) 'terminal-marshal.c'; else $(CYGPATH_W) '$(srcdir)/terminal-marshal.c'; fi` + +mate_terminal-terminal-type-builtins.o: terminal-type-builtins.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-type-builtins.o -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-type-builtins.Tpo -c -o mate_terminal-terminal-type-builtins.o `test -f 'terminal-type-builtins.c' || echo '$(srcdir)/'`terminal-type-builtins.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-type-builtins.Tpo $(DEPDIR)/mate_terminal-terminal-type-builtins.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-type-builtins.c' object='mate_terminal-terminal-type-builtins.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-type-builtins.o `test -f 'terminal-type-builtins.c' || echo '$(srcdir)/'`terminal-type-builtins.c + +mate_terminal-terminal-type-builtins.obj: terminal-type-builtins.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -MT mate_terminal-terminal-type-builtins.obj -MD -MP -MF $(DEPDIR)/mate_terminal-terminal-type-builtins.Tpo -c -o mate_terminal-terminal-type-builtins.obj `if test -f 'terminal-type-builtins.c'; then $(CYGPATH_W) 'terminal-type-builtins.c'; else $(CYGPATH_W) '$(srcdir)/terminal-type-builtins.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mate_terminal-terminal-type-builtins.Tpo $(DEPDIR)/mate_terminal-terminal-type-builtins.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='terminal-type-builtins.c' object='mate_terminal-terminal-type-builtins.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mate_terminal_CPPFLAGS) $(CPPFLAGS) $(mate_terminal_CFLAGS) $(CFLAGS) -c -o mate_terminal-terminal-type-builtins.obj `if test -f 'terminal-type-builtins.c'; then $(CYGPATH_W) 'terminal-type-builtins.c'; else $(CYGPATH_W) '$(srcdir)/terminal-type-builtins.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-aboutDATA: $(about_DATA) + @$(NORMAL_INSTALL) + test -z "$(aboutdir)" || $(MKDIR_P) "$(DESTDIR)$(aboutdir)" + @list='$(about_DATA)'; test -n "$(aboutdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(aboutdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(aboutdir)" || exit $$?; \ + done + +uninstall-aboutDATA: + @$(NORMAL_UNINSTALL) + @list='$(about_DATA)'; test -n "$(aboutdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(aboutdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(aboutdir)" && rm -f $$files +install-builderDATA: $(builder_DATA) + @$(NORMAL_INSTALL) + test -z "$(builderdir)" || $(MKDIR_P) "$(DESTDIR)$(builderdir)" + @list='$(builder_DATA)'; test -n "$(builderdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(builderdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(builderdir)" || exit $$?; \ + done + +uninstall-builderDATA: + @$(NORMAL_UNINSTALL) + @list='$(builder_DATA)'; test -n "$(builderdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(builderdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(builderdir)" && rm -f $$files +install-schemaDATA: $(schema_DATA) + @$(NORMAL_INSTALL) + test -z "$(schemadir)" || $(MKDIR_P) "$(DESTDIR)$(schemadir)" + @list='$(schema_DATA)'; test -n "$(schemadir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(schemadir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(schemadir)" || exit $$?; \ + done + +uninstall-schemaDATA: + @$(NORMAL_UNINSTALL) + @list='$(schema_DATA)'; test -n "$(schemadir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(schemadir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(schemadir)" && rm -f $$files +install-uimanagerDATA: $(uimanager_DATA) + @$(NORMAL_INSTALL) + test -z "$(uimanagerdir)" || $(MKDIR_P) "$(DESTDIR)$(uimanagerdir)" + @list='$(uimanager_DATA)'; test -n "$(uimanagerdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(uimanagerdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(uimanagerdir)" || exit $$?; \ + done + +uninstall-uimanagerDATA: + @$(NORMAL_UNINSTALL) + @list='$(uimanager_DATA)'; test -n "$(uimanagerdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(uimanagerdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(uimanagerdir)" && rm -f $$files + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +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: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + 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: ctags-recursive $(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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-recursive +all-am: Makefile $(PROGRAMS) $(DATA) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(aboutdir)" "$(DESTDIR)$(builderdir)" "$(DESTDIR)$(schemadir)" "$(DESTDIR)$(uimanagerdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +@MATECONF_SCHEMAS_INSTALL_FALSE@install-data-local: +clean: clean-recursive + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-aboutDATA install-builderDATA \ + install-data-local install-schemaDATA install-uimanagerDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-aboutDATA uninstall-binPROGRAMS \ + uninstall-builderDATA uninstall-schemaDATA \ + uninstall-uimanagerDATA + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all check \ + ctags-recursive install install-am install-strip \ + tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool ctags ctags-recursive distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-aboutDATA install-am install-binPROGRAMS \ + install-builderDATA install-data install-data-am \ + install-data-local 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-schemaDATA install-strip \ + install-uimanagerDATA installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-aboutDATA uninstall-am \ + uninstall-binPROGRAMS uninstall-builderDATA \ + uninstall-schemaDATA uninstall-uimanagerDATA + + +terminal-type-builtins.h: stamp-terminal-type-builtins.h + @true + +stamp-terminal-type-builtins.h: terminal-type-builtins.h.template $(TYPES_H_FILES) + $(AM_V_GEN) $(GLIB_MKENUMS) --template $< $(filter-out $<,$^) > xgen-ttbh \ + && (cmp -s xgen-ttbh terminal-type-builtins.h || cp xgen-ttbh terminal-type-builtins.h ) \ + && rm -f xgen-ttbh \ + && echo timestamp > $(@F) + +terminal-type-builtins.c: terminal-type-builtins.c.template $(TYPES_H_FILES) + $(AM_V_GEN) $(GLIB_MKENUMS) --template $< $(filter-out $<,$^) > xgen-ttbc \ + && (cmp -s xgen-ttbc terminal-type-builtins.c || cp xgen-ttbc terminal-type-builtins.c ) \ + && rm -f xgen-ttbc + +terminal-marshal.h: $(srcdir)/terminal-marshal.list + $(AM_V_GEN) ( $(GLIB_GENMARSHAL) --prefix=_terminal_marshal $(srcdir)/terminal-marshal.list \ + --header \ + --internal > terminal-marshal.h.tmp \ + && mv terminal-marshal.h.tmp terminal-marshal.h ) \ + || ( rm -f terminal-marshal.h.tmp && exit 1 ) + +terminal-marshal.c: $(srcdir)/terminal-marshal.list + $(AM_V_GEN) ( $(GLIB_GENMARSHAL) --prefix=_terminal_marshal $(srcdir)/terminal-marshal.list \ + --header \ + --body \ + --internal > terminal-marshal.c.tmp \ + && mv terminal-marshal.c.tmp terminal-marshal.c ) \ + || ( rm -f terminal-marshal.c.tmp && exit 1 ) + +@INTLTOOL_SCHEMAS_RULE@ + +@MATECONF_SCHEMAS_INSTALL_TRUE@install-data-local: +@MATECONF_SCHEMAS_INSTALL_TRUE@ MATECONF_CONFIG_SOURCE=$(MATECONF_SCHEMA_CONFIG_SOURCE) $(MATECONFTOOL) --makefile-install-rule $(top_builddir)/src/$(schema_DATA) + +%.ui: %.glade + $(AM_V_GEN) $(GTK_BUILDER_CONVERT) $< $@ + +-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/src/eggdesktopfile.c b/src/eggdesktopfile.c new file mode 100644 index 0000000..e38d867 --- /dev/null +++ b/src/eggdesktopfile.c @@ -0,0 +1,1510 @@ +/* eggdesktopfile.c - Freedesktop.Org Desktop Files + * Copyright (C) 2007 Novell, Inc. + * + * Based on mate-desktop-item.c + * Copyright (C) 1999, 2000 Red Hat Inc. + * Copyright (C) 2001 George Lebl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - + * Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "eggdesktopfile.h" + +#include <string.h> +#include <unistd.h> + +#include <glib/gi18n.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +struct EggDesktopFile { + GKeyFile *key_file; + char *source; + + char *name, *icon; + EggDesktopFileType type; + char document_code; +}; + +/** + * egg_desktop_file_new: + * @desktop_file_path: path to a Freedesktop-style Desktop file + * @error: error pointer + * + * Creates a new #EggDesktopFile for @desktop_file. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new (const char *desktop_file_path, GError **error) +{ + GKeyFile *key_file; + + key_file = g_key_file_new (); + if (!g_key_file_load_from_file (key_file, desktop_file_path, 0, error)) + { + g_key_file_free (key_file); + return NULL; + } + + return egg_desktop_file_new_from_key_file (key_file, desktop_file_path, + error); +} + +/** + * egg_desktop_file_new_from_data_dirs: + * @desktop_file_path: relative path to a Freedesktop-style Desktop file + * @error: error pointer + * + * Looks for @desktop_file_path in the paths returned from + * g_get_user_data_dir() and g_get_system_data_dirs(), and creates + * a new #EggDesktopFile from it. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_data_dirs (const char *desktop_file_path, + GError **error) +{ + EggDesktopFile *desktop_file; + GKeyFile *key_file; + char *full_path; + + key_file = g_key_file_new (); + if (!g_key_file_load_from_data_dirs (key_file, desktop_file_path, + &full_path, 0, error)) + { + g_key_file_free (key_file); + return NULL; + } + + desktop_file = egg_desktop_file_new_from_key_file (key_file, + full_path, + error); + g_free (full_path); + return desktop_file; +} + +/** + * egg_desktop_file_new_from_dirs: + * @desktop_file_path: relative path to a Freedesktop-style Desktop file + * @search_dirs: NULL-terminated array of directories to search + * @error: error pointer + * + * Looks for @desktop_file_path in the paths returned from + * g_get_user_data_dir() and g_get_system_data_dirs(), and creates + * a new #EggDesktopFile from it. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_dirs (const char *desktop_file_path, + const char **search_dirs, + GError **error) +{ + EggDesktopFile *desktop_file; + GKeyFile *key_file; + char *full_path; + + key_file = g_key_file_new (); + if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs, + &full_path, 0, error)) + { + g_key_file_free (key_file); + return NULL; + } + + desktop_file = egg_desktop_file_new_from_key_file (key_file, + full_path, + error); + g_free (full_path); + return desktop_file; +} + +/** + * egg_desktop_file_new_from_key_file: + * @key_file: a #GKeyFile representing a desktop file + * @source: the path or URI that @key_file was loaded from, or %NULL + * @error: error pointer + * + * Creates a new #EggDesktopFile for @key_file. Assumes ownership of + * @key_file (on success or failure); you should consider @key_file to + * be freed after calling this function. + * + * Return value: the new #EggDesktopFile, or %NULL on error. + **/ +EggDesktopFile * +egg_desktop_file_new_from_key_file (GKeyFile *key_file, + const char *source, + GError **error) +{ + EggDesktopFile *desktop_file; + char *version, *type; + + if (!g_key_file_has_group (key_file, EGG_DESKTOP_FILE_GROUP)) + { + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_INVALID, + _("File is not a valid .desktop file")); + g_key_file_free (key_file); + return NULL; + } + + version = g_key_file_get_value (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_VERSION, + NULL); + if (version) + { + double version_num; + char *end; + + version_num = g_ascii_strtod (version, &end); + if (*end) + { + g_warning ("Invalid Version string '%s' in %s", + version, source ? source : "(unknown)"); + } + else if (version_num > 1.0) + { + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_INVALID, + _("Unrecognized desktop file Version '%s'"), version); + g_free (version); + g_key_file_free (key_file); + return NULL; + } + g_free (version); + } + + desktop_file = g_new0 (EggDesktopFile, 1); + desktop_file->key_file = key_file; + + if (g_path_is_absolute (source)) + desktop_file->source = g_filename_to_uri (source, NULL, NULL); + else + desktop_file->source = g_strdup (source); + + desktop_file->name = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_NAME, error); + if (!desktop_file->name) + { + egg_desktop_file_free (desktop_file); + return NULL; + } + + type = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_TYPE, error); + if (!type) + { + egg_desktop_file_free (desktop_file); + return NULL; + } + + if (!strcmp (type, "Application")) + { + char *exec, *p; + + desktop_file->type = EGG_DESKTOP_FILE_TYPE_APPLICATION; + + exec = g_key_file_get_string (key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_EXEC, + error); + if (!exec) + { + egg_desktop_file_free (desktop_file); + g_free (type); + return NULL; + } + + /* See if it takes paths or URIs or neither */ + for (p = exec; *p; p++) + { + if (*p == '%') + { + if (p[1] == '\0' || strchr ("FfUu", p[1])) + { + desktop_file->document_code = p[1]; + break; + } + p++; + } + } + + g_free (exec); + } + else if (!strcmp (type, "Link")) + { + char *url; + + desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK; + + url = g_key_file_get_string (key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_URL, + error); + if (!url) + { + egg_desktop_file_free (desktop_file); + g_free (type); + return NULL; + } + g_free (url); + } + else if (!strcmp (type, "Directory")) + desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY; + else + desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED; + + g_free (type); + + /* Check the Icon key */ + desktop_file->icon = g_key_file_get_string (key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_ICON, + NULL); + if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon)) + { + char *ext; + + /* Lots of .desktop files still get this wrong */ + ext = strrchr (desktop_file->icon, '.'); + if (ext && (!strcmp (ext, ".png") || + !strcmp (ext, ".xpm") || + !strcmp (ext, ".svg"))) + { + g_warning ("Desktop file '%s' has malformed Icon key '%s'" + "(should not include extension)", + source ? source : "(unknown)", + desktop_file->icon); + *ext = '\0'; + } + } + + return desktop_file; +} + +/** + * egg_desktop_file_free: + * @desktop_file: an #EggDesktopFile + * + * Frees @desktop_file. + **/ +void +egg_desktop_file_free (EggDesktopFile *desktop_file) +{ + g_key_file_free (desktop_file->key_file); + g_free (desktop_file->source); + g_free (desktop_file->name); + g_free (desktop_file->icon); + g_free (desktop_file); +} + +/** + * egg_desktop_file_get_source: + * @desktop_file: an #EggDesktopFile + * + * Gets the URI that @desktop_file was loaded from. + * + * Return value: @desktop_file's source URI + **/ +const char * +egg_desktop_file_get_source (EggDesktopFile *desktop_file) +{ + return desktop_file->source; +} + +/** + * egg_desktop_file_get_desktop_file_type: + * @desktop_file: an #EggDesktopFile + * + * Gets the desktop file type of @desktop_file. + * + * Return value: @desktop_file's type + **/ +EggDesktopFileType +egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file) +{ + return desktop_file->type; +} + +/** + * egg_desktop_file_get_name: + * @desktop_file: an #EggDesktopFile + * + * Gets the (localized) value of @desktop_file's "Name" key. + * + * Return value: the application/link name + **/ +const char * +egg_desktop_file_get_name (EggDesktopFile *desktop_file) +{ + return desktop_file->name; +} + +/** + * egg_desktop_file_get_icon: + * @desktop_file: an #EggDesktopFile + * + * Gets the value of @desktop_file's "Icon" key. + * + * If the icon string is a full path (that is, if g_path_is_absolute() + * returns %TRUE when called on it), it points to a file containing an + * unthemed icon. If the icon string is not a full path, it is the + * name of a themed icon, which can be looked up with %GtkIconTheme, + * or passed directly to a theme-aware widget like %GtkImage or + * %GtkCellRendererPixbuf. + * + * Return value: the icon path or name + **/ +const char * +egg_desktop_file_get_icon (EggDesktopFile *desktop_file) +{ + return desktop_file->icon; +} + +gboolean +egg_desktop_file_has_key (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_has_key (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +char * +egg_desktop_file_get_string (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +char * +egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + GError **error) +{ + return g_key_file_get_locale_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, locale, + error); +} + +gboolean +egg_desktop_file_get_boolean (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_get_boolean (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +double +egg_desktop_file_get_numeric (EggDesktopFile *desktop_file, + const char *key, + GError **error) +{ + return g_key_file_get_double (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + error); +} + +char ** +egg_desktop_file_get_string_list (EggDesktopFile *desktop_file, + const char *key, + gsize *length, + GError **error) +{ + return g_key_file_get_string_list (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, length, + error); +} + +char ** +egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + gsize *length, + GError **error) +{ + return g_key_file_get_locale_string_list (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, key, + locale, length, + error); +} + +/** + * egg_desktop_file_can_launch: + * @desktop_file: an #EggDesktopFile + * @desktop_environment: the name of the running desktop environment, + * or %NULL + * + * Tests if @desktop_file can/should be launched in the current + * environment. If @desktop_environment is non-%NULL, @desktop_file's + * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that + * this desktop_file is appropriate for the named environment. + * + * Furthermore, if @desktop_file has type + * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is + * also checked, to make sure the binary it points to exists. + * + * egg_desktop_file_can_launch() does NOT check the value of the + * "Hidden" key. + * + * Return value: %TRUE if @desktop_file can be launched + **/ +gboolean +egg_desktop_file_can_launch (EggDesktopFile *desktop_file, + const char *desktop_environment) +{ + char *try_exec, *found_program; + char **only_show_in, **not_show_in; + gboolean found; + int i; + + if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION && + desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK) + return FALSE; + + if (desktop_environment) + { + only_show_in = g_key_file_get_string_list (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN, + NULL, NULL); + if (only_show_in) + { + for (i = 0, found = FALSE; only_show_in[i] && !found; i++) + { + if (!strcmp (only_show_in[i], desktop_environment)) + found = TRUE; + } + + g_strfreev (only_show_in); + + if (!found) + return FALSE; + } + + not_show_in = g_key_file_get_string_list (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN, + NULL, NULL); + if (not_show_in) + { + for (i = 0, found = FALSE; not_show_in[i] && !found; i++) + { + if (!strcmp (not_show_in[i], desktop_environment)) + found = TRUE; + } + + g_strfreev (not_show_in); + + if (found) + return FALSE; + } + } + + if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION) + { + try_exec = g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_TRY_EXEC, + NULL); + if (try_exec) + { + found_program = g_find_program_in_path (try_exec); + g_free (try_exec); + + if (!found_program) + return FALSE; + g_free (found_program); + } + } + + return TRUE; +} + +/** + * egg_desktop_file_accepts_documents: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file represents an application that can accept + * documents on the command line. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file) +{ + return desktop_file->document_code != 0; +} + +/** + * egg_desktop_file_accepts_multiple: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file can accept multiple documents at once. + * + * If this returns %FALSE, you can still pass multiple documents to + * egg_desktop_file_launch(), but that will result in multiple copies + * of the application being launched. See egg_desktop_file_launch() + * for more details. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file) +{ + return (desktop_file->document_code == 'F' || + desktop_file->document_code == 'U'); +} + +/** + * egg_desktop_file_accepts_uris: + * @desktop_file: an #EggDesktopFile + * + * Tests if @desktop_file can accept (non-"file:") URIs as documents to + * open. + * + * Return value: %TRUE or %FALSE + **/ +gboolean +egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file) +{ + return (desktop_file->document_code == 'U' || + desktop_file->document_code == 'u'); +} + +static void +append_quoted_word (GString *str, + const char *s, + gboolean in_single_quotes, + gboolean in_double_quotes) +{ + const char *p; + + if (!in_single_quotes && !in_double_quotes) + g_string_append_c (str, '\''); + else if (!in_single_quotes && in_double_quotes) + g_string_append (str, "\"'"); + + if (!strchr (s, '\'')) + g_string_append (str, s); + else + { + for (p = s; *p != '\0'; p++) + { + if (*p == '\'') + g_string_append (str, "'\\''"); + else + g_string_append_c (str, *p); + } + } + + if (!in_single_quotes && !in_double_quotes) + g_string_append_c (str, '\''); + else if (!in_single_quotes && in_double_quotes) + g_string_append (str, "'\""); +} + +static void +do_percent_subst (EggDesktopFile *desktop_file, + char code, + GString *str, + GSList **documents, + gboolean in_single_quotes, + gboolean in_double_quotes) +{ + GSList *d; + char *doc; + + switch (code) + { + case '%': + g_string_append_c (str, '%'); + break; + + case 'F': + case 'U': + for (d = *documents; d; d = d->next) + { + doc = d->data; + g_string_append (str, " "); + append_quoted_word (str, doc, in_single_quotes, in_double_quotes); + } + *documents = NULL; + break; + + case 'f': + case 'u': + if (*documents) + { + doc = (*documents)->data; + g_string_append (str, " "); + append_quoted_word (str, doc, in_single_quotes, in_double_quotes); + *documents = (*documents)->next; + } + break; + + case 'i': + if (desktop_file->icon) + { + g_string_append (str, "--icon "); + append_quoted_word (str, desktop_file->icon, + in_single_quotes, in_double_quotes); + } + break; + + case 'c': + if (desktop_file->name) + { + append_quoted_word (str, desktop_file->name, + in_single_quotes, in_double_quotes); + } + break; + + case 'k': + if (desktop_file->source) + { + append_quoted_word (str, desktop_file->source, + in_single_quotes, in_double_quotes); + } + break; + + case 'D': + case 'N': + case 'd': + case 'n': + case 'v': + case 'm': + /* Deprecated; skip */ + break; + + default: + g_warning ("Unrecognized %%-code '%%%c' in Exec", code); + break; + } +} + +static char * +parse_exec (EggDesktopFile *desktop_file, + GSList **documents, + GError **error) +{ + char *exec, *p, *command; + gboolean escape, single_quot, double_quot; + GString *gs; + + exec = g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_EXEC, + error); + if (!exec) + return NULL; + + /* Build the command */ + gs = g_string_new (NULL); + escape = single_quot = double_quot = FALSE; + + for (p = exec; *p != '\0'; p++) + { + if (escape) + { + escape = FALSE; + g_string_append_c (gs, *p); + } + else if (*p == '\\') + { + if (!single_quot) + escape = TRUE; + g_string_append_c (gs, *p); + } + else if (*p == '\'') + { + g_string_append_c (gs, *p); + if (!single_quot && !double_quot) + single_quot = TRUE; + else if (single_quot) + single_quot = FALSE; + } + else if (*p == '"') + { + g_string_append_c (gs, *p); + if (!single_quot && !double_quot) + double_quot = TRUE; + else if (double_quot) + double_quot = FALSE; + } + else if (*p == '%' && p[1]) + { + do_percent_subst (desktop_file, p[1], gs, documents, + single_quot, double_quot); + p++; + } + else + g_string_append_c (gs, *p); + } + + g_free (exec); + command = g_string_free (gs, FALSE); + + /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */ + if (g_key_file_has_key (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_TERMINAL, + NULL)) + { + GError *terminal_error = NULL; + gboolean use_terminal = + g_key_file_get_boolean (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_TERMINAL, + &terminal_error); + if (terminal_error) + { + g_free (command); + g_propagate_error (error, terminal_error); + return NULL; + } + + if (use_terminal) + { + gs = g_string_new ("xdg-terminal "); + append_quoted_word (gs, command, FALSE, FALSE); + g_free (command); + command = g_string_free (gs, FALSE); + } + } + + return command; +} + +static GSList * +translate_document_list (EggDesktopFile *desktop_file, GSList *documents) +{ + gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file); + GSList *ret, *d; + + for (d = documents, ret = NULL; d; d = d->next) + { + const char *document = d->data; + gboolean is_uri = !g_path_is_absolute (document); + char *translated; + + if (accepts_uris) + { + if (is_uri) + translated = g_strdup (document); + else + translated = g_filename_to_uri (document, NULL, NULL); + } + else + { + if (is_uri) + translated = g_filename_from_uri (document, NULL, NULL); + else + translated = g_strdup (document); + } + + if (translated) + ret = g_slist_prepend (ret, translated); + } + + return g_slist_reverse (ret); +} + +static void +free_document_list (GSList *documents) +{ + GSList *d; + + for (d = documents; d; d = d->next) + g_free (d->data); + g_slist_free (documents); +} + +/** + * egg_desktop_file_parse_exec: + * @desktop_file: a #EggDesktopFile + * @documents: a list of document paths or URIs + * @error: error pointer + * + * Parses @desktop_file's Exec key, inserting @documents into it, and + * returns the result. + * + * If @documents contains non-file: URIs and @desktop_file does not + * accept URIs, those URIs will be ignored. Likewise, if @documents + * contains more elements than @desktop_file accepts, the extra + * documents will be ignored. + * + * Return value: the parsed Exec string + **/ +char * +egg_desktop_file_parse_exec (EggDesktopFile *desktop_file, + GSList *documents, + GError **error) +{ + GSList *translated, *docs; + char *command; + + docs = translated = translate_document_list (desktop_file, documents); + command = parse_exec (desktop_file, &docs, error); + free_document_list (translated); + + return command; +} + +static gboolean +parse_link (EggDesktopFile *desktop_file, + EggDesktopFile **app_desktop_file, + GSList **documents, + GError **error) +{ + char *url; + GKeyFile *key_file; + + url = g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_URL, + error); + if (!url) + return FALSE; + *documents = g_slist_prepend (NULL, url); + + /* FIXME: use gvfs */ + key_file = g_key_file_new (); + g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_NAME, + "xdg-open"); + g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_TYPE, + "Application"); + g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_EXEC, + "xdg-open %u"); + *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL); + return TRUE; +} + +#if GTK_CHECK_VERSION (2, 12, 0) +static char * +start_startup_notification (GdkDisplay *display, + EggDesktopFile *desktop_file, + const char *argv0, + int screen, + int workspace, + guint32 launch_time) +{ + static int sequence = 0; + char *startup_id; + char *description, *wmclass; + char *screen_str, *workspace_str; + + if (g_key_file_has_key (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY, + NULL)) + { + if (!g_key_file_get_boolean (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY, + NULL)) + return NULL; + wmclass = NULL; + } + else + { + wmclass = g_key_file_get_string (desktop_file->key_file, + EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS, + NULL); + if (!wmclass) + return NULL; + } + + if (launch_time == (guint32)-1) + launch_time = gdk_x11_display_get_user_time (display); + startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu", + g_get_prgname (), + (unsigned long)getpid (), + g_get_host_name (), + argv0, + sequence++, + (unsigned long)launch_time); + + description = g_strdup_printf (_("Starting %s"), desktop_file->name); + screen_str = g_strdup_printf ("%d", screen); + workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace); + + gdk_x11_display_broadcast_startup_message (display, "new", + "ID", startup_id, + "NAME", desktop_file->name, + "SCREEN", screen_str, + "BIN", argv0, + "ICON", desktop_file->icon, + "DESKTOP", workspace_str, + "DESCRIPTION", description, + "WMCLASS", wmclass, + NULL); + + g_free (description); + g_free (wmclass); + g_free (screen_str); + g_free (workspace_str); + + return startup_id; +} + +static void +end_startup_notification (GdkDisplay *display, + const char *startup_id) +{ + gdk_x11_display_broadcast_startup_message (display, "remove", + "ID", startup_id, + NULL); +} + +#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */) + +typedef struct { + GdkDisplay *display; + char *startup_id; +} StartupNotificationData; + +static gboolean +startup_notification_timeout (gpointer data) +{ + StartupNotificationData *sn_data = data; + + end_startup_notification (sn_data->display, sn_data->startup_id); + g_object_unref (sn_data->display); + g_free (sn_data->startup_id); + g_free (sn_data); + + return FALSE; +} + +static void +set_startup_notification_timeout (GdkDisplay *display, + const char *startup_id) +{ + StartupNotificationData *sn_data; + + sn_data = g_new (StartupNotificationData, 1); + sn_data->display = g_object_ref (display); + sn_data->startup_id = g_strdup (startup_id); + + g_timeout_add_seconds (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH, + startup_notification_timeout, sn_data); +} +#endif /* GTK 2.12 */ + +static GPtrArray * +array_putenv (GPtrArray *env, char *variable) +{ + guint i, keylen; + + if (!env) + { + char **envp; + + env = g_ptr_array_new (); + + envp = g_listenv (); + for (i = 0; envp[i]; i++) + { + const char *value; + + value = g_getenv (envp[i]); + g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i], + value ? value : "")); + } + g_strfreev (envp); + } + + keylen = strcspn (variable, "="); + + /* Remove old value of key */ + for (i = 0; i < env->len; i++) + { + char *envvar = env->pdata[i]; + + if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=') + { + g_free (envvar); + g_ptr_array_remove_index_fast (env, i); + break; + } + } + + /* Add new value */ + g_ptr_array_add (env, g_strdup (variable)); + + return env; +} + +static gboolean +egg_desktop_file_launchv (EggDesktopFile *desktop_file, + GSList *documents, va_list args, + GError **error) +{ + EggDesktopFileLaunchOption option; + GSList *translated_documents = NULL, *docs = NULL; + char *command, **argv; + int argc, i, screen_num; + gboolean success, current_success; + GdkDisplay *display; + char *startup_id; + + GPtrArray *env = NULL; + char **variables = NULL; + GdkScreen *screen = NULL; + int workspace = -1; + const char *directory = NULL; + guint32 launch_time = (guint32)-1; + GSpawnFlags flags = G_SPAWN_SEARCH_PATH; + GSpawnChildSetupFunc setup_func = NULL; + gpointer setup_data = NULL; + + GPid *ret_pid = NULL; + int *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL; + char **ret_startup_id = NULL; + + if (documents && desktop_file->document_code == 0) + { + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, + _("Application does not accept documents on command line")); + return FALSE; + } + + /* Read the options: technically it's incorrect for the caller to + * NULL-terminate the list of options (rather than 0-terminating + * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED, + * it's more consistent with other glib/gtk methods, and it will + * work as long as sizeof (int) <= sizeof (NULL), and NULL is + * represented as 0. (Which is true everywhere we care about.) + */ + while ((option = va_arg (args, EggDesktopFileLaunchOption))) + { + switch (option) + { + case EGG_DESKTOP_FILE_LAUNCH_CLEARENV: + if (env) + g_ptr_array_free (env, TRUE); + env = g_ptr_array_new (); + break; + case EGG_DESKTOP_FILE_LAUNCH_PUTENV: + variables = va_arg (args, char **); + for (i = 0; variables[i]; i++) + env = array_putenv (env, variables[i]); + break; + + case EGG_DESKTOP_FILE_LAUNCH_SCREEN: + screen = va_arg (args, GdkScreen *); + break; + case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: + workspace = va_arg (args, int); + break; + + case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: + directory = va_arg (args, const char *); + break; + case EGG_DESKTOP_FILE_LAUNCH_TIME: + launch_time = va_arg (args, guint32); + break; + case EGG_DESKTOP_FILE_LAUNCH_FLAGS: + flags |= va_arg (args, GSpawnFlags); + /* Make sure they didn't set any flags that don't make sense. */ + flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO; + break; + case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC: + setup_func = va_arg (args, GSpawnChildSetupFunc); + setup_data = va_arg (args, gpointer); + break; + + case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID: + ret_pid = va_arg (args, GPid *); + break; + case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE: + ret_stdin = va_arg (args, int *); + break; + case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE: + ret_stdout = va_arg (args, int *); + break; + case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE: + ret_stderr = va_arg (args, int *); + break; + case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID: + ret_startup_id = va_arg (args, char **); + break; + + default: + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION, + _("Unrecognized launch option: %d"), + GPOINTER_TO_INT (option)); + success = FALSE; + goto out; + } + } + + if (screen) + { + char *display_name = gdk_screen_make_display_name (screen); + char *display_env = g_strdup_printf ("DISPLAY=%s", display_name); + env = array_putenv (env, display_env); + g_free (display_name); + g_free (display_env); + + display = gdk_screen_get_display (screen); + } + else + { + display = gdk_display_get_default (); + screen = gdk_display_get_default_screen (display); + } + screen_num = gdk_screen_get_number (screen); + + translated_documents = translate_document_list (desktop_file, documents); + docs = translated_documents; + + success = FALSE; + + do + { + command = parse_exec (desktop_file, &docs, error); + if (!command) + goto out; + + if (!g_shell_parse_argv (command, &argc, &argv, error)) + { + g_free (command); + goto out; + } + g_free (command); + +#if GTK_CHECK_VERSION (2, 12, 0) + startup_id = start_startup_notification (display, desktop_file, + argv[0], screen_num, + workspace, launch_time); + if (startup_id) + { + char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s", + startup_id); + env = array_putenv (env, startup_id_env); + g_free (startup_id_env); + } +#else + startup_id = NULL; +#endif /* GTK 2.12 */ + + if (env != NULL) + g_ptr_array_add (env, NULL); + + current_success = + g_spawn_async_with_pipes (directory, + argv, + env ? (char **)(env->pdata) : NULL, + flags, + setup_func, setup_data, + ret_pid, + ret_stdin, ret_stdout, ret_stderr, + error); + g_strfreev (argv); + + if (startup_id) + { +#if GTK_CHECK_VERSION (2, 12, 0) + if (current_success) + { + set_startup_notification_timeout (display, startup_id); + + if (ret_startup_id) + *ret_startup_id = startup_id; + else + g_free (startup_id); + } + else +#endif /* GTK 2.12 */ + g_free (startup_id); + } + else if (ret_startup_id) + *ret_startup_id = NULL; + + if (current_success) + { + /* If we successfully launch any instances of the app, make + * sure we return TRUE and don't set @error. + */ + success = TRUE; + error = NULL; + + /* Also, only set the output params on the first one */ + ret_pid = NULL; + ret_stdin = ret_stdout = ret_stderr = NULL; + ret_startup_id = NULL; + } + } + while (docs && current_success); + + out: + if (env) + { + g_ptr_array_foreach (env, (GFunc)g_free, NULL); + g_ptr_array_free (env, TRUE); + } + free_document_list (translated_documents); + + return success; +} + +/** + * egg_desktop_file_launch: + * @desktop_file: an #EggDesktopFile + * @documents: a list of URIs or paths to documents to open + * @error: error pointer + * @...: additional options + * + * Launches @desktop_file with the given arguments. Additional options + * can be specified as follows: + * + * %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments) + * clears the environment in the child process + * %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables) + * adds the NAME=VALUE strings in the given %NULL-terminated + * array to the child process's environment + * %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen) + * causes the application to be launched on the given screen + * %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace) + * causes the application to be launched on the given workspace + * %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir) + * causes the application to be launched in the given directory + * %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time) + * sets the "launch time" for the application. If the user + * interacts with another window after @launch_time but before + * the launched application creates its first window, the window + * manager may choose to not give focus to the new application. + * Passing 0 for @launch_time will explicitly request that the + * application not receive focus. + * %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags) + * Sets additional #GSpawnFlags to use. See g_spawn_async() for + * more details. + * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer) + * Sets the child setup callback and the data to pass to it. + * (See g_spawn_async() for more details.) + * + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid) + * On a successful launch, sets *@pid to the PID of the launched + * application. + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id) + * On a successful launch, sets *@startup_id to the Startup + * Notification "startup id" of the launched application. + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd) + * On a successful launch, sets *@fd to the file descriptor of + * a pipe connected to the application's stdin. + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd) + * On a successful launch, sets *@fd to the file descriptor of + * a pipe connected to the application's stdout. + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd) + * On a successful launch, sets *@fd to the file descriptor of + * a pipe connected to the application's stderr. + * + * The options should be terminated with a single %NULL. + * + * If @documents contains multiple documents, but + * egg_desktop_file_accepts_multiple() returns %FALSE for + * @desktop_file, then egg_desktop_file_launch() will actually launch + * multiple instances of the application. In that case, the return + * value (as well as any values passed via + * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the + * first instance of the application that was launched (but the + * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each + * instance). + * + * Return value: %TRUE if the application was successfully launched. + **/ +gboolean +egg_desktop_file_launch (EggDesktopFile *desktop_file, + GSList *documents, GError **error, + ...) +{ + va_list args; + gboolean success; + EggDesktopFile *app_desktop_file; + + switch (desktop_file->type) + { + case EGG_DESKTOP_FILE_TYPE_APPLICATION: + va_start (args, error); + success = egg_desktop_file_launchv (desktop_file, documents, + args, error); + va_end (args); + break; + + case EGG_DESKTOP_FILE_TYPE_LINK: + if (documents) + { + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, + _("Can't pass document URIs to a 'Type=Link' desktop entry")); + return FALSE; + } + + if (!parse_link (desktop_file, &app_desktop_file, &documents, error)) + return FALSE; + + va_start (args, error); + success = egg_desktop_file_launchv (app_desktop_file, documents, + args, error); + va_end (args); + + egg_desktop_file_free (app_desktop_file); + free_document_list (documents); + break; + + case EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED: + case EGG_DESKTOP_FILE_TYPE_DIRECTORY: + default: + g_set_error (error, EGG_DESKTOP_FILE_ERROR, + EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, + _("Not a launchable item")); + success = FALSE; + break; + } + + return success; +} + + +GQuark +egg_desktop_file_error_quark (void) +{ + return g_quark_from_static_string ("egg-desktop_file-error-quark"); +} + + +G_LOCK_DEFINE_STATIC (egg_desktop_file); +static EggDesktopFile *egg_desktop_file; + +static void +egg_set_desktop_file_internal (const char *desktop_file_path, + gboolean set_defaults) +{ + GError *error = NULL; + + G_LOCK (egg_desktop_file); + if (egg_desktop_file) + egg_desktop_file_free (egg_desktop_file); + + egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error); + if (error) + { + g_warning ("Could not load desktop file '%s': %s", + desktop_file_path, error->message); + g_error_free (error); + } + + if (set_defaults && egg_desktop_file != NULL) { + /* Set localized application name and default window icon */ + if (egg_desktop_file->name) + g_set_application_name (egg_desktop_file->name); + if (egg_desktop_file->icon) + { + if (g_path_is_absolute (egg_desktop_file->icon)) + gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL); + else + gtk_window_set_default_icon_name (egg_desktop_file->icon); + } + } + + G_UNLOCK (egg_desktop_file); +} + +/** + * egg_set_desktop_file: + * @desktop_file_path: path to the application's desktop file + * + * Creates an #EggDesktopFile for the application from the data at + * @desktop_file_path. This will also call g_set_application_name() + * with the localized application name from the desktop file, and + * gtk_window_set_default_icon_name() or + * gtk_window_set_default_icon_from_file() with the application's + * icon. Other code may use additional information from the desktop + * file. + * See egg_set_desktop_file_without_defaults() for a variant of this + * function that does not set the application name and default window + * icon. + * + * Note that for thread safety reasons, this function can only + * be called once, and is mutually exclusive with calling + * egg_set_desktop_file_without_defaults(). + **/ +void +egg_set_desktop_file (const char *desktop_file_path) +{ + egg_set_desktop_file_internal (desktop_file_path, TRUE); +} + +/** + * egg_set_desktop_file_without_defaults: + * @desktop_file_path: path to the application's desktop file + * + * Creates an #EggDesktopFile for the application from the data at + * @desktop_file_path. + * See egg_set_desktop_file() for a variant of this function that + * sets the application name and default window icon from the information + * in the desktop file. + * + * Note that for thread safety reasons, this function can only + * be called once, and is mutually exclusive with calling + * egg_set_desktop_file(). + **/ +void +egg_set_desktop_file_without_defaults (const char *desktop_file_path) +{ + egg_set_desktop_file_internal (desktop_file_path, FALSE); +} + +/** + * egg_get_desktop_file: + * + * Gets the application's #EggDesktopFile, as set by + * egg_set_desktop_file(). + * + * Return value: the #EggDesktopFile, or %NULL if it hasn't been set. + **/ +EggDesktopFile * +egg_get_desktop_file (void) +{ + EggDesktopFile *retval; + + G_LOCK (egg_desktop_file); + retval = egg_desktop_file; + G_UNLOCK (egg_desktop_file); + + return retval; +} diff --git a/src/eggdesktopfile.h b/src/eggdesktopfile.h new file mode 100644 index 0000000..17bd96e --- /dev/null +++ b/src/eggdesktopfile.h @@ -0,0 +1,160 @@ +/* eggdesktopfile.h - Freedesktop.Org Desktop Files + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - + * Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_DESKTOP_FILE_H__ +#define __EGG_DESKTOP_FILE_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct EggDesktopFile EggDesktopFile; + +typedef enum { + EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED, + + EGG_DESKTOP_FILE_TYPE_APPLICATION, + EGG_DESKTOP_FILE_TYPE_LINK, + EGG_DESKTOP_FILE_TYPE_DIRECTORY +} EggDesktopFileType; + +EggDesktopFile *egg_desktop_file_new (const char *desktop_file_path, + GError **error); + +EggDesktopFile *egg_desktop_file_new_from_data_dirs (const char *desktop_file_path, + GError **error); +EggDesktopFile *egg_desktop_file_new_from_dirs (const char *desktop_file_path, + const char **search_dirs, + GError **error); +EggDesktopFile *egg_desktop_file_new_from_key_file (GKeyFile *key_file, + const char *source, + GError **error); + +void egg_desktop_file_free (EggDesktopFile *desktop_file); + +const char *egg_desktop_file_get_source (EggDesktopFile *desktop_file); + +EggDesktopFileType egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file); + +const char *egg_desktop_file_get_name (EggDesktopFile *desktop_file); +const char *egg_desktop_file_get_icon (EggDesktopFile *desktop_file); + +gboolean egg_desktop_file_can_launch (EggDesktopFile *desktop_file, + const char *desktop_environment); + +gboolean egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file); +gboolean egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file); +gboolean egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file); + +char *egg_desktop_file_parse_exec (EggDesktopFile *desktop_file, + GSList *documents, + GError **error); + +gboolean egg_desktop_file_launch (EggDesktopFile *desktop_file, + GSList *documents, + GError **error, + ...) G_GNUC_NULL_TERMINATED; + +typedef enum { + EGG_DESKTOP_FILE_LAUNCH_CLEARENV = 1, + EGG_DESKTOP_FILE_LAUNCH_PUTENV, + EGG_DESKTOP_FILE_LAUNCH_SCREEN, + EGG_DESKTOP_FILE_LAUNCH_WORKSPACE, + EGG_DESKTOP_FILE_LAUNCH_DIRECTORY, + EGG_DESKTOP_FILE_LAUNCH_TIME, + EGG_DESKTOP_FILE_LAUNCH_FLAGS, + EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC, + EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, + EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE, + EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE, + EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE, + EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID +} EggDesktopFileLaunchOption; + +/* Standard Keys */ +#define EGG_DESKTOP_FILE_GROUP "Desktop Entry" + +#define EGG_DESKTOP_FILE_KEY_TYPE "Type" +#define EGG_DESKTOP_FILE_KEY_VERSION "Version" +#define EGG_DESKTOP_FILE_KEY_NAME "Name" +#define EGG_DESKTOP_FILE_KEY_GENERIC_NAME "GenericName" +#define EGG_DESKTOP_FILE_KEY_NO_DISPLAY "NoDisplay" +#define EGG_DESKTOP_FILE_KEY_COMMENT "Comment" +#define EGG_DESKTOP_FILE_KEY_ICON "Icon" +#define EGG_DESKTOP_FILE_KEY_HIDDEN "Hidden" +#define EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN "OnlyShowIn" +#define EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN "NotShowIn" +#define EGG_DESKTOP_FILE_KEY_TRY_EXEC "TryExec" +#define EGG_DESKTOP_FILE_KEY_EXEC "Exec" +#define EGG_DESKTOP_FILE_KEY_PATH "Path" +#define EGG_DESKTOP_FILE_KEY_TERMINAL "Terminal" +#define EGG_DESKTOP_FILE_KEY_MIME_TYPE "MimeType" +#define EGG_DESKTOP_FILE_KEY_CATEGORIES "Categories" +#define EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY "StartupNotify" +#define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS "StartupWMClass" +#define EGG_DESKTOP_FILE_KEY_URL "URL" + +/* Accessors */ +gboolean egg_desktop_file_has_key (EggDesktopFile *desktop_file, + const char *key, + GError **error); +char *egg_desktop_file_get_string (EggDesktopFile *desktop_file, + const char *key, + GError **error) G_GNUC_MALLOC; +char *egg_desktop_file_get_locale_string (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + GError **error) G_GNUC_MALLOC; +gboolean egg_desktop_file_get_boolean (EggDesktopFile *desktop_file, + const char *key, + GError **error); +double egg_desktop_file_get_numeric (EggDesktopFile *desktop_file, + const char *key, + GError **error); +char **egg_desktop_file_get_string_list (EggDesktopFile *desktop_file, + const char *key, + gsize *length, + GError **error) G_GNUC_MALLOC; +char **egg_desktop_file_get_locale_string_list (EggDesktopFile *desktop_file, + const char *key, + const char *locale, + gsize *length, + GError **error) G_GNUC_MALLOC; + + +/* Errors */ +#define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark() + +GQuark egg_desktop_file_error_quark (void); + +typedef enum { + EGG_DESKTOP_FILE_ERROR_INVALID, + EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE, + EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION +} EggDesktopFileError; + +/* Global application desktop file */ +void egg_set_desktop_file (const char *desktop_file_path); +void egg_set_desktop_file_without_defaults (const char *desktop_file_path); +EggDesktopFile *egg_get_desktop_file (void); + + +G_END_DECLS + +#endif /* __EGG_DESKTOP_FILE_H__ */ diff --git a/src/eggshell.c b/src/eggshell.c new file mode 100644 index 0000000..ff4d2ce --- /dev/null +++ b/src/eggshell.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * Copyright (C) 1999, 2000 Red Hat, Inc. + * All rights reserved. + * + * This file is part of the Mate Library. + * + * The Mate Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * The Mate Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Mate Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ + +/* + * + * Mate utility routines. + * (C) 1997, 1998, 1999 the Free Software Foundation. + * + * Author: Miguel de Icaza, + */ + +#include <config.h> + +#include "eggshell.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#ifndef G_OS_WIN32 +#include <pwd.h> +#endif + +#include <glib.h> + +/** + * egg_shell: + * @shell: the value of the SHELL env variable + * + * Retrieves the user's preferred shell. + * + * Returns: A newly allocated string that is the path to the shell. + */ +char * +egg_shell (const char *shell) +{ +#ifndef G_OS_WIN32 + struct passwd *pw; + int i; + static const char shells [][14] = { + /* Note that on some systems shells can also + * be installed in /usr/bin */ + "/bin/bash", "/usr/bin/bash", + "/bin/zsh", "/usr/bin/zsh", + "/bin/tcsh", "/usr/bin/tcsh", + "/bin/ksh", "/usr/bin/ksh", + "/bin/csh", "/bin/sh" + }; + + if (geteuid () == getuid () && + getegid () == getgid ()) { + /* only in non-setuid */ + if (shell != NULL) { + if (access (shell, X_OK) == 0) { + return g_strdup (shell); + } + } + } + pw = getpwuid(getuid()); + if (pw && pw->pw_shell) { + if (access (pw->pw_shell, X_OK) == 0) { + return g_strdup (pw->pw_shell); + } + } + + for (i = 0; i != G_N_ELEMENTS (shells); i++) { + if (access (shells [i], X_OK) == 0) { + return g_strdup (shells[i]); + } + } + + /* If /bin/sh doesn't exist, your system is truly broken. */ + g_assert_not_reached (); + + /* Placate compiler. */ + return NULL; +#else + /* g_find_program_in_path() always looks also in the Windows + * and System32 directories, so it should always find either cmd.exe + * or command.com. + */ + char *retval = g_find_program_in_path ("cmd.exe"); + + if (retval == NULL) + retval = g_find_program_in_path ("command.com"); + + g_assert (retval != NULL); + + return retval; +#endif +} diff --git a/src/eggshell.h b/src/eggshell.h new file mode 100644 index 0000000..a4d4c4b --- /dev/null +++ b/src/eggshell.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation + * Copyright (C) 1999, 2000 Red Hat, Inc. + * All rights reserved. + * + * This file is part of the Mate Library. + * + * The Mate Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * The Mate Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Mate Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + @NOTATION@ + */ + +#ifndef __EGG_USER_SHELL_H__ +#define __EGG_USER_SHELL_H__ + +#include <stdlib.h> +#include <glib.h> + +G_BEGIN_DECLS + +/* Find the name of the user's shell. */ +char *egg_shell (const char *shell); + +G_END_DECLS + +#endif diff --git a/src/eggsmclient-osx.c b/src/eggsmclient-osx.c new file mode 100644 index 0000000..e6fa376 --- /dev/null +++ b/src/eggsmclient-osx.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* EggSMClientOSX + * + * For details on the OS X logout process, see: + * http://developer.apple.com/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/BootProcess.html#//apple_ref/doc/uid/20002130-114618 + * + * EggSMClientOSX registers for the kAEQuitApplication AppleEvent; the + * handler we register (quit_requested()) will be invoked from inside + * the quartz event-handling code (specifically, from inside + * [NSApplication nextEventMatchingMask]) when an AppleEvent arrives. + * We use AESuspendTheCurrentEvent() and AEResumeTheCurrentEvent() to + * allow asynchronous / non-main-loop-reentering processing of the + * quit request. (These are part of the Carbon framework; it doesn't + * seem to be possible to handle AppleEvents asynchronously from + * Cocoa.) + */ + +#include "config.h" + +#include "eggsmclient-private.h" +#include <gdk/gdkquartz.h> +#include <Carbon/Carbon.h> +#include <CoreServices/CoreServices.h> + +#define EGG_TYPE_SM_CLIENT_OSX (egg_sm_client_osx_get_type ()) +#define EGG_SM_CLIENT_OSX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSX)) +#define EGG_SM_CLIENT_OSX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass)) +#define EGG_IS_SM_CLIENT_OSX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_OSX)) +#define EGG_IS_SM_CLIENT_OSX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_OSX)) +#define EGG_SM_CLIENT_OSX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_OSX, EggSMClientOSXClass)) + +typedef struct _EggSMClientOSX EggSMClientOSX; +typedef struct _EggSMClientOSXClass EggSMClientOSXClass; + +struct _EggSMClientOSX { + EggSMClient parent; + + AppleEvent quit_event, quit_reply; + gboolean quit_requested, quitting; +}; + +struct _EggSMClientOSXClass +{ + EggSMClientClass parent_class; + +}; + +static void sm_client_osx_startup (EggSMClient *client, + const char *client_id); +static void sm_client_osx_will_quit (EggSMClient *client, + gboolean will_quit); +static gboolean sm_client_osx_end_session (EggSMClient *client, + EggSMClientEndStyle style, + gboolean request_confirmation); + +static pascal OSErr quit_requested (const AppleEvent *, AppleEvent *, long); + +G_DEFINE_TYPE (EggSMClientOSX, egg_sm_client_osx, EGG_TYPE_SM_CLIENT) + +static void +egg_sm_client_osx_init (EggSMClientOSX *osx) +{ + ; +} + +static void +egg_sm_client_osx_class_init (EggSMClientOSXClass *klass) +{ + EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); + + sm_client_class->startup = sm_client_osx_startup; + sm_client_class->will_quit = sm_client_osx_will_quit; + sm_client_class->end_session = sm_client_osx_end_session; +} + +EggSMClient * +egg_sm_client_osx_new (void) +{ + return g_object_new (EGG_TYPE_SM_CLIENT_OSX, NULL); +} + +static void +sm_client_osx_startup (EggSMClient *client, + const char *client_id) +{ + AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, + NewAEEventHandlerUPP (quit_requested), + (long)GPOINTER_TO_SIZE (client), false); +} + +static gboolean +idle_quit_requested (gpointer client) +{ + egg_sm_client_quit_requested (client); + return FALSE; +} + +static pascal OSErr +quit_requested (const AppleEvent *aevt, AppleEvent *reply, long refcon) +{ + EggSMClient *client = GSIZE_TO_POINTER ((gsize)refcon); + EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon); + + g_return_val_if_fail (!osx->quit_requested, userCanceledErr); + + /* FIXME AEInteractWithUser? */ + + osx->quit_requested = TRUE; + AEDuplicateDesc (aevt, &osx->quit_event); + AEDuplicateDesc (reply, &osx->quit_reply); + AESuspendTheCurrentEvent (aevt); + + /* Don't emit the "quit_requested" signal immediately, since we're + * called from a weird point in the guts of gdkeventloop-quartz.c + */ + g_idle_add (idle_quit_requested, client); + return noErr; +} + +static pascal OSErr +quit_requested_resumed (const AppleEvent *aevt, AppleEvent *reply, long refcon) +{ + EggSMClientOSX *osx = GSIZE_TO_POINTER ((gsize)refcon); + + osx->quit_requested = FALSE; + return osx->quitting ? noErr : userCanceledErr; +} + +static gboolean +idle_will_quit (gpointer client) +{ + EggSMClientOSX *osx = (EggSMClientOSX *)client; + + /* Resume the event with a new handler that will return a value to + * the system. + */ + AEResumeTheCurrentEvent (&osx->quit_event, &osx->quit_reply, + NewAEEventHandlerUPP (quit_requested_resumed), + (long)GPOINTER_TO_SIZE (client)); + AEDisposeDesc (&osx->quit_event); + AEDisposeDesc (&osx->quit_reply); + + if (osx->quitting) + egg_sm_client_quit (client); + return FALSE; +} + +static void +sm_client_osx_will_quit (EggSMClient *client, + gboolean will_quit) +{ + EggSMClientOSX *osx = (EggSMClientOSX *)client; + + g_return_if_fail (osx->quit_requested); + + osx->quitting = will_quit; + + /* Finish in an idle handler since the caller might have called + * egg_sm_client_will_quit() from inside the "quit_requested" signal + * handler, but may not expect the "quit" signal to arrive during + * the _will_quit() call. + */ + g_idle_add (idle_will_quit, client); +} + +static gboolean +sm_client_osx_end_session (EggSMClient *client, + EggSMClientEndStyle style, + gboolean request_confirmation) +{ + static const ProcessSerialNumber loginwindow_psn = { 0, kSystemProcess }; + AppleEvent event = { typeNull, NULL }, reply = { typeNull, NULL }; + AEAddressDesc target; + AEEventID id; + OSErr err; + + switch (style) + { + case EGG_SM_CLIENT_END_SESSION_DEFAULT: + case EGG_SM_CLIENT_LOGOUT: + id = request_confirmation ? kAELogOut : kAEReallyLogOut; + break; + case EGG_SM_CLIENT_REBOOT: + id = request_confirmation ? kAEShowRestartDialog : kAERestart; + break; + case EGG_SM_CLIENT_SHUTDOWN: + id = request_confirmation ? kAEShowShutdownDialog : kAEShutDown; + break; + } + + err = AECreateDesc (typeProcessSerialNumber, &loginwindow_psn, + sizeof (loginwindow_psn), &target); + if (err != noErr) + { + g_warning ("Could not create descriptor for loginwindow: %d", err); + return FALSE; + } + + err = AECreateAppleEvent (kCoreEventClass, id, &target, + kAutoGenerateReturnID, kAnyTransactionID, + &event); + AEDisposeDesc (&target); + if (err != noErr) + { + g_warning ("Could not create logout AppleEvent: %d", err); + return FALSE; + } + + err = AESend (&event, &reply, kAENoReply, kAENormalPriority, + kAEDefaultTimeout, NULL, NULL); + AEDisposeDesc (&event); + if (err == noErr) + AEDisposeDesc (&reply); + + return err == noErr; +} diff --git a/src/eggsmclient-private.h b/src/eggsmclient-private.h new file mode 100644 index 0000000..d1e39cb --- /dev/null +++ b/src/eggsmclient-private.h @@ -0,0 +1,53 @@ +/* eggsmclient-private.h + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_SM_CLIENT_PRIVATE_H__ +#define __EGG_SM_CLIENT_PRIVATE_H__ + +#include <gdkconfig.h> +#include "eggsmclient.h" + +G_BEGIN_DECLS + +GKeyFile *egg_sm_client_save_state (EggSMClient *client); +void egg_sm_client_quit_requested (EggSMClient *client); +void egg_sm_client_quit_cancelled (EggSMClient *client); +void egg_sm_client_quit (EggSMClient *client); + +#if defined (GDK_WINDOWING_X11) +# ifdef EGG_SM_CLIENT_BACKEND_XSMP +GType egg_sm_client_xsmp_get_type (void); +EggSMClient *egg_sm_client_xsmp_new (void); +# endif +# ifdef EGG_SM_CLIENT_BACKEND_DBUS +GType egg_sm_client_dbus_get_type (void); +EggSMClient *egg_sm_client_dbus_new (void); +# endif +#elif defined (GDK_WINDOWING_WIN32) +GType egg_sm_client_win32_get_type (void); +EggSMClient *egg_sm_client_win32_new (void); +#elif defined (GDK_WINDOWING_QUARTZ) +GType egg_sm_client_osx_get_type (void); +EggSMClient *egg_sm_client_osx_new (void); +#endif + +G_END_DECLS + + +#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */ diff --git a/src/eggsmclient-win32.c b/src/eggsmclient-win32.c new file mode 100644 index 0000000..a762c6c --- /dev/null +++ b/src/eggsmclient-win32.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* EggSMClientWin32 + * + * For details on the Windows XP logout process, see: + * http://msdn.microsoft.com/en-us/library/aa376876.aspx. + * + * Vista adds some new APIs which EggSMClient does not make use of; see + * http://msdn.microsoft.com/en-us/library/ms700677(VS.85).aspx + * + * When shutting down, Windows sends every top-level window a + * WM_QUERYENDSESSION event, which the application must respond to + * synchronously, saying whether or not it will quit. To avoid main + * loop re-entrancy problems (and to avoid having to muck about too + * much with the guts of the gdk-win32 main loop), we watch for this + * event in a separate thread, which then signals the main thread and + * waits for the main thread to handle the event. Since we don't want + * to require g_thread_init() to be called, we do this all using + * Windows-specific thread methods. + * + * After the application handles the WM_QUERYENDSESSION event, + * Windows then sends it a WM_ENDSESSION event with a TRUE or FALSE + * parameter indicating whether the session is or is not actually + * going to end now. We handle this from the other thread as well. + * + * As mentioned above, Vista introduces several additional new APIs + * that don't fit into the (current) EggSMClient API. Windows also has + * an entirely separate shutdown-notification scheme for non-GUI apps, + * which we also don't handle here. + */ + +#include "config.h" + +#include "eggsmclient-private.h" +#include <gdk/gdk.h> + +#define WIN32_LEAN_AND_MEAN +#define UNICODE +#include <windows.h> +#include <process.h> + +#define EGG_TYPE_SM_CLIENT_WIN32 (egg_sm_client_win32_get_type ()) +#define EGG_SM_CLIENT_WIN32(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32)) +#define EGG_SM_CLIENT_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32Class)) +#define EGG_IS_SM_CLIENT_WIN32(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_WIN32)) +#define EGG_IS_SM_CLIENT_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_WIN32)) +#define EGG_SM_CLIENT_WIN32_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_WIN32, EggSMClientWin32Class)) + +typedef struct _EggSMClientWin32 EggSMClientWin32; +typedef struct _EggSMClientWin32Class EggSMClientWin32Class; + +struct _EggSMClientWin32 { + EggSMClient parent; + + HANDLE message_event, response_event; + + volatile GSourceFunc event; + volatile gboolean will_quit; +}; + +struct _EggSMClientWin32Class +{ + EggSMClientClass parent_class; + +}; + +static void sm_client_win32_startup (EggSMClient *client, + const char *client_id); +static void sm_client_win32_will_quit (EggSMClient *client, + gboolean will_quit); +static gboolean sm_client_win32_end_session (EggSMClient *client, + EggSMClientEndStyle style, + gboolean request_confirmation); + +static GSource *g_win32_handle_source_add (HANDLE handle, GSourceFunc callback, + gpointer user_data); +static gboolean got_message (gpointer user_data); +static void sm_client_thread (gpointer data); + +G_DEFINE_TYPE (EggSMClientWin32, egg_sm_client_win32, EGG_TYPE_SM_CLIENT) + +static void +egg_sm_client_win32_init (EggSMClientWin32 *win32) +{ + ; +} + +static void +egg_sm_client_win32_class_init (EggSMClientWin32Class *klass) +{ + EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); + + sm_client_class->startup = sm_client_win32_startup; + sm_client_class->will_quit = sm_client_win32_will_quit; + sm_client_class->end_session = sm_client_win32_end_session; +} + +EggSMClient * +egg_sm_client_win32_new (void) +{ + return g_object_new (EGG_TYPE_SM_CLIENT_WIN32, NULL); +} + +static void +sm_client_win32_startup (EggSMClient *client, + const char *client_id) +{ + EggSMClientWin32 *win32 = (EggSMClientWin32 *)client; + + win32->message_event = CreateEvent (NULL, FALSE, FALSE, NULL); + win32->response_event = CreateEvent (NULL, FALSE, FALSE, NULL); + g_win32_handle_source_add (win32->message_event, got_message, win32); + _beginthread (sm_client_thread, 0, client); +} + +static void +sm_client_win32_will_quit (EggSMClient *client, + gboolean will_quit) +{ + EggSMClientWin32 *win32 = (EggSMClientWin32 *)client; + + win32->will_quit = will_quit; + SetEvent (win32->response_event); +} + +static gboolean +sm_client_win32_end_session (EggSMClient *client, + EggSMClientEndStyle style, + gboolean request_confirmation) +{ + UINT uFlags = EWX_LOGOFF; + + switch (style) + { + case EGG_SM_CLIENT_END_SESSION_DEFAULT: + case EGG_SM_CLIENT_LOGOUT: + uFlags = EWX_LOGOFF; + break; + case EGG_SM_CLIENT_REBOOT: + uFlags = EWX_REBOOT; + break; + case EGG_SM_CLIENT_SHUTDOWN: + uFlags = EWX_POWEROFF; + break; + } + + /* There's no way to make ExitWindowsEx() show a logout dialog, so + * we ignore @request_confirmation. + */ + +#ifdef SHTDN_REASON_FLAG_PLANNED + ExitWindowsEx (uFlags, SHTDN_REASON_FLAG_PLANNED); +#else + ExitWindowsEx (uFlags, 0); +#endif + + return TRUE; +} + + +/* callbacks from logout-listener thread */ + +static gboolean +emit_quit_requested (gpointer smclient) +{ + gdk_threads_enter (); + egg_sm_client_quit_requested (smclient); + gdk_threads_leave (); + + return FALSE; +} + +static gboolean +emit_quit (gpointer smclient) +{ + EggSMClientWin32 *win32 = smclient; + + gdk_threads_enter (); + egg_sm_client_quit (smclient); + gdk_threads_leave (); + + SetEvent (win32->response_event); + return FALSE; +} + +static gboolean +emit_quit_cancelled (gpointer smclient) +{ + EggSMClientWin32 *win32 = smclient; + + gdk_threads_enter (); + egg_sm_client_quit_cancelled (smclient); + gdk_threads_leave (); + + SetEvent (win32->response_event); + return FALSE; +} + +static gboolean +got_message (gpointer smclient) +{ + EggSMClientWin32 *win32 = smclient; + + win32->event (win32); + return TRUE; +} + +/* Windows HANDLE GSource */ + +typedef struct { + GSource source; + GPollFD pollfd; +} GWin32HandleSource; + +static gboolean +g_win32_handle_source_prepare (GSource *source, gint *timeout) +{ + *timeout = -1; + return FALSE; +} + +static gboolean +g_win32_handle_source_check (GSource *source) +{ + GWin32HandleSource *hsource = (GWin32HandleSource *)source; + + return hsource->pollfd.revents; +} + +static gboolean +g_win32_handle_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) +{ + return (*callback) (user_data); +} + +static void +g_win32_handle_source_finalize (GSource *source) +{ + ; +} + +GSourceFuncs g_win32_handle_source_funcs = { + g_win32_handle_source_prepare, + g_win32_handle_source_check, + g_win32_handle_source_dispatch, + g_win32_handle_source_finalize +}; + +static GSource * +g_win32_handle_source_add (HANDLE handle, GSourceFunc callback, gpointer user_data) +{ + GWin32HandleSource *hsource; + GSource *source; + + source = g_source_new (&g_win32_handle_source_funcs, sizeof (GWin32HandleSource)); + hsource = (GWin32HandleSource *)source; + hsource->pollfd.fd = (int)handle; + hsource->pollfd.events = G_IO_IN; + hsource->pollfd.revents = 0; + g_source_add_poll (source, &hsource->pollfd); + + g_source_set_callback (source, callback, user_data, NULL); + g_source_attach (source, NULL); + return source; +} + +/* logout-listener thread */ + +LRESULT CALLBACK +sm_client_win32_window_procedure (HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + EggSMClientWin32 *win32 = + (EggSMClientWin32 *)GetWindowLongPtr (hwnd, GWLP_USERDATA); + + switch (message) + { + case WM_QUERYENDSESSION: + win32->event = emit_quit_requested; + SetEvent (win32->message_event); + + WaitForSingleObject (win32->response_event, INFINITE); + return win32->will_quit; + + case WM_ENDSESSION: + if (wParam) + { + /* The session is ending */ + win32->event = emit_quit; + } + else + { + /* Nope, the session *isn't* ending */ + win32->event = emit_quit_cancelled; + } + + SetEvent (win32->message_event); + WaitForSingleObject (win32->response_event, INFINITE); + + return 0; + + default: + return DefWindowProc (hwnd, message, wParam, lParam); + } +} + +static void +sm_client_thread (gpointer smclient) +{ + HINSTANCE instance; + WNDCLASSEXW wcl; + ATOM klass; + HWND window; + MSG msg; + + instance = GetModuleHandle (NULL); + + memset (&wcl, 0, sizeof (WNDCLASSEX)); + wcl.cbSize = sizeof (WNDCLASSEX); + wcl.lpfnWndProc = sm_client_win32_window_procedure; + wcl.hInstance = instance; + wcl.lpszClassName = L"EggSmClientWindow"; + klass = RegisterClassEx (&wcl); + + window = CreateWindowEx (0, MAKEINTRESOURCE (klass), + L"EggSmClientWindow", 0, + 10, 10, 50, 50, GetDesktopWindow (), + NULL, instance, NULL); + SetWindowLongPtr (window, GWLP_USERDATA, (LONG_PTR)smclient); + + /* main loop */ + while (GetMessage (&msg, NULL, 0, 0)) + DispatchMessage (&msg); +} diff --git a/src/eggsmclient-xsmp.c b/src/eggsmclient-xsmp.c new file mode 100644 index 0000000..9cf343b --- /dev/null +++ b/src/eggsmclient-xsmp.c @@ -0,0 +1,1405 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * + * Inspired by various other pieces of code including GsmClient (C) + * 2001 Havoc Pennington, MateClient (C) 1998 Carsten Schaar, and twm + * session code (C) 1998 The Open Group. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "eggsmclient.h" +#include "eggsmclient-private.h" + +#include "eggdesktopfile.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <X11/SM/SMlib.h> + +#include <gdk/gdk.h> + +#define EGG_TYPE_SM_CLIENT_XSMP (egg_sm_client_xsmp_get_type ()) +#define EGG_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP)) +#define EGG_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass)) +#define EGG_IS_SM_CLIENT_XSMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_XSMP)) +#define EGG_IS_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_XSMP)) +#define EGG_SM_CLIENT_XSMP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass)) + +typedef struct _EggSMClientXSMP EggSMClientXSMP; +typedef struct _EggSMClientXSMPClass EggSMClientXSMPClass; + +/* These mostly correspond to the similarly-named states in section + * 9.1 of the XSMP spec. Some of the states there aren't represented + * here, because we don't need them. SHUTDOWN_CANCELLED is slightly + * different from the spec; we use it when the client is IDLE after a + * ShutdownCancelled message, but the application is still interacting + * and doesn't know the shutdown has been cancelled yet. + */ +typedef enum +{ + XSMP_STATE_IDLE, + XSMP_STATE_SAVE_YOURSELF, + XSMP_STATE_INTERACT_REQUEST, + XSMP_STATE_INTERACT, + XSMP_STATE_SAVE_YOURSELF_DONE, + XSMP_STATE_SHUTDOWN_CANCELLED, + XSMP_STATE_CONNECTION_CLOSED +} EggSMClientXSMPState; + +static const char *state_names[] = { + "idle", + "save-yourself", + "interact-request", + "interact", + "save-yourself-done", + "shutdown-cancelled", + "connection-closed" +}; + +#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state]) + +struct _EggSMClientXSMP +{ + EggSMClient parent; + + SmcConn connection; + char *client_id; + + EggSMClientXSMPState state; + char **restart_command; + gboolean set_restart_command; + int restart_style; + char **discard_command; + gboolean set_discard_command; + + guint idle; + + /* Current SaveYourself state */ + guint expecting_initial_save_yourself : 1; + guint need_save_state : 1; + guint need_quit_requested : 1; + guint interact_errors : 1; + guint shutting_down : 1; + + /* Todo list */ + guint waiting_to_set_initial_properties : 1; + guint waiting_to_emit_quit : 1; + guint waiting_to_emit_quit_cancelled : 1; + guint waiting_to_save_myself : 1; + +}; + +struct _EggSMClientXSMPClass +{ + EggSMClientClass parent_class; + +}; + +static void sm_client_xsmp_startup (EggSMClient *client, + const char *client_id); +static void sm_client_xsmp_set_restart_command (EggSMClient *client, + int argc, + const char **argv); +static void sm_client_xsmp_set_discard_command (EggSMClient *client, + int argc, + const char **argv); +static void sm_client_xsmp_will_quit (EggSMClient *client, + gboolean will_quit); +static gboolean sm_client_xsmp_end_session (EggSMClient *client, + EggSMClientEndStyle style, + gboolean request_confirmation); + +static void xsmp_save_yourself (SmcConn smc_conn, + SmPointer client_data, + int save_style, + Bool shutdown, + int interact_style, + Bool fast); +static void xsmp_die (SmcConn smc_conn, + SmPointer client_data); +static void xsmp_save_complete (SmcConn smc_conn, + SmPointer client_data); +static void xsmp_shutdown_cancelled (SmcConn smc_conn, + SmPointer client_data); +static void xsmp_interact (SmcConn smc_conn, + SmPointer client_data); + +static SmProp *array_prop (const char *name, + ...); +static SmProp *ptrarray_prop (const char *name, + GPtrArray *values); +static SmProp *string_prop (const char *name, + const char *value); +static SmProp *card8_prop (const char *name, + unsigned char value); + +static void set_properties (EggSMClientXSMP *xsmp, ...); +static void delete_properties (EggSMClientXSMP *xsmp, ...); + +static GPtrArray *generate_command (char **argv, + const char *client_id, + const char *state_file); + +static void save_state (EggSMClientXSMP *xsmp); +static void do_save_yourself (EggSMClientXSMP *xsmp); +static void update_pending_events (EggSMClientXSMP *xsmp); + +static void ice_init (void); +static gboolean process_ice_messages (IceConn ice_conn); +static void smc_error_handler (SmcConn smc_conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + SmPointer values); + +G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT) + +static void +egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp) +{ + xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + xsmp->connection = NULL; + xsmp->restart_style = SmRestartIfRunning; +} + +static void +egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass) +{ + EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); + + sm_client_class->startup = sm_client_xsmp_startup; + sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command; + sm_client_class->set_discard_command = sm_client_xsmp_set_discard_command; + sm_client_class->will_quit = sm_client_xsmp_will_quit; + sm_client_class->end_session = sm_client_xsmp_end_session; +} + +EggSMClient * +egg_sm_client_xsmp_new (void) +{ + if (!g_getenv ("SESSION_MANAGER")) + return NULL; + + return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL); +} + +static gboolean +sm_client_xsmp_set_initial_properties (gpointer user_data) +{ + EggSMClientXSMP *xsmp = user_data; + EggDesktopFile *desktop_file; + GPtrArray *clone, *restart; + char pid_str[64]; + + if (xsmp->idle) + { + g_source_remove (xsmp->idle); + xsmp->idle = 0; + } + xsmp->waiting_to_set_initial_properties = FALSE; + + if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART) + xsmp->restart_style = SmRestartNever; + + /* Parse info out of desktop file */ + desktop_file = egg_get_desktop_file (); + if (desktop_file) + { + GError *err = NULL; + char *cmdline, **argv; + int argc; + + if (xsmp->restart_style == SmRestartIfRunning) + { + if (egg_desktop_file_get_boolean (desktop_file, + "X-MATE-AutoRestart", NULL)) + xsmp->restart_style = SmRestartImmediately; + } + + if (!xsmp->set_restart_command) + { + cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err); + if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err)) + { + egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp), + argc, (const char **)argv); + g_strfreev (argv); + } + else + { + g_warning ("Could not parse Exec line in desktop file: %s", + err->message); + g_error_free (err); + } + g_free (cmdline); + } + } + + if (!xsmp->set_restart_command) + xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1); + + clone = generate_command (xsmp->restart_command, NULL, NULL); + restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); + + g_debug ("Setting initial properties"); + + /* Program, CloneCommand, RestartCommand, and UserID are required. + * ProcessID isn't required, but the SM may be able to do something + * useful with it. + */ + g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ()); + set_properties (xsmp, + string_prop (SmProgram, g_get_prgname ()), + ptrarray_prop (SmCloneCommand, clone), + ptrarray_prop (SmRestartCommand, restart), + string_prop (SmUserID, g_get_user_name ()), + string_prop (SmProcessID, pid_str), + card8_prop (SmRestartStyleHint, xsmp->restart_style), + NULL); + g_ptr_array_free (clone, TRUE); + g_ptr_array_free (restart, TRUE); + + if (desktop_file) + { + set_properties (xsmp, + string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)), + NULL); + } + + update_pending_events (xsmp); + return FALSE; +} + +/* This gets called from two different places: xsmp_die() (when the + * server asks us to disconnect) and process_ice_messages() (when the + * server disconnects unexpectedly). + */ +static void +sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp) +{ + SmcConn connection; + + if (!xsmp->connection) + return; + + g_debug ("Disconnecting"); + + connection = xsmp->connection; + xsmp->connection = NULL; + SmcCloseConnection (connection, 0, NULL); + xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + + xsmp->waiting_to_save_myself = FALSE; + update_pending_events (xsmp); +} + +static void +sm_client_xsmp_startup (EggSMClient *client, + const char *client_id) +{ + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + SmcCallbacks callbacks; + char *ret_client_id; + char error_string_ret[256]; + + xsmp->client_id = g_strdup (client_id); + + ice_init (); + SmcSetErrorHandler (smc_error_handler); + + callbacks.save_yourself.callback = xsmp_save_yourself; + callbacks.die.callback = xsmp_die; + callbacks.save_complete.callback = xsmp_save_complete; + callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled; + + callbacks.save_yourself.client_data = xsmp; + callbacks.die.client_data = xsmp; + callbacks.save_complete.client_data = xsmp; + callbacks.shutdown_cancelled.client_data = xsmp; + + client_id = NULL; + error_string_ret[0] = '\0'; + xsmp->connection = + SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor, + SmcSaveYourselfProcMask | SmcDieProcMask | + SmcSaveCompleteProcMask | + SmcShutdownCancelledProcMask, + &callbacks, + xsmp->client_id, &ret_client_id, + sizeof (error_string_ret), error_string_ret); + + if (!xsmp->connection) + { + g_warning ("Failed to connect to the session manager: %s\n", + error_string_ret[0] ? + error_string_ret : "no error message given"); + xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + return; + } + + /* We expect a pointless initial SaveYourself if either (a) we + * didn't have an initial client ID, or (b) we DID have an initial + * client ID, but the server rejected it and gave us a new one. + */ + if (!xsmp->client_id || + (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0)) + xsmp->expecting_initial_save_yourself = TRUE; + + if (ret_client_id) + { + g_free (xsmp->client_id); + xsmp->client_id = g_strdup (ret_client_id); + free (ret_client_id); + + gdk_threads_enter (); + gdk_set_sm_client_id (xsmp->client_id); + gdk_threads_leave (); + + g_debug ("Got client ID \"%s\"", xsmp->client_id); + } + + xsmp->state = XSMP_STATE_IDLE; + + /* Do not set the initial properties until we reach the main loop, + * so that the application has a chance to call + * egg_set_desktop_file(). (This may also help the session manager + * have a better idea of when the application is fully up and + * running.) + */ + xsmp->waiting_to_set_initial_properties = TRUE; + xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client); +} + +static void +sm_client_xsmp_set_restart_command (EggSMClient *client, + int argc, + const char **argv) +{ + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + int i; + + g_strfreev (xsmp->restart_command); + + xsmp->restart_command = g_new (char *, argc + 1); + for (i = 0; i < argc; i++) + xsmp->restart_command[i] = g_strdup (argv[i]); + xsmp->restart_command[i] = NULL; + + xsmp->set_restart_command = TRUE; +} + +static void +sm_client_xsmp_set_discard_command (EggSMClient *client, + int argc, + const char **argv) +{ + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + int i; + + g_strfreev (xsmp->discard_command); + + xsmp->discard_command = g_new (char *, argc + 1); + for (i = 0; i < argc; i++) + xsmp->discard_command[i] = g_strdup (argv[i]); + xsmp->discard_command[i] = NULL; + + xsmp->set_discard_command = TRUE; +} + +static void +sm_client_xsmp_will_quit (EggSMClient *client, + gboolean will_quit) +{ + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + + if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED) + { + /* The session manager has already exited! Schedule a quit + * signal. + */ + xsmp->waiting_to_emit_quit = TRUE; + update_pending_events (xsmp); + return; + } + else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) + { + /* We received a ShutdownCancelled message while the application + * was interacting; Schedule a quit_cancelled signal. + */ + xsmp->waiting_to_emit_quit_cancelled = TRUE; + update_pending_events (xsmp); + return; + } + + g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT); + + g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True"); + SmcInteractDone (xsmp->connection, !will_quit); + + if (will_quit && xsmp->need_save_state) + save_state (xsmp); + + g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False"); + SmcSaveYourselfDone (xsmp->connection, will_quit); + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +} + +static gboolean +sm_client_xsmp_end_session (EggSMClient *client, + EggSMClientEndStyle style, + gboolean request_confirmation) +{ + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + int save_type; + + /* To end the session via XSMP, we have to send a + * SaveYourselfRequest. We aren't allowed to do that if anything + * else is going on, but we don't want to expose this fact to the + * application. So we do our best to patch things up here... + * + * In the worst case, this method might block for some length of + * time in process_ice_messages, but the only time that code path is + * honestly likely to get hit is if the application tries to end the + * session as the very first thing it does, in which case it + * probably won't actually block anyway. It's not worth gunking up + * the API to try to deal nicely with the other 0.01% of cases where + * this happens. + */ + + while (xsmp->state != XSMP_STATE_IDLE || + xsmp->expecting_initial_save_yourself) + { + /* If we're already shutting down, we don't need to do anything. */ + if (xsmp->shutting_down) + return TRUE; + + switch (xsmp->state) + { + case XSMP_STATE_CONNECTION_CLOSED: + return FALSE; + + case XSMP_STATE_SAVE_YOURSELF: + /* Trying to log out from the save_state callback? Whatever. + * Abort the save_state. + */ + SmcSaveYourselfDone (xsmp->connection, FALSE); + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; + break; + + case XSMP_STATE_INTERACT_REQUEST: + case XSMP_STATE_INTERACT: + case XSMP_STATE_SHUTDOWN_CANCELLED: + /* Already in a shutdown-related state, just ignore + * the new shutdown request... + */ + return TRUE; + + case XSMP_STATE_IDLE: + if (xsmp->waiting_to_set_initial_properties) + sm_client_xsmp_set_initial_properties (xsmp); + + if (!xsmp->expecting_initial_save_yourself) + break; + /* else fall through */ + + case XSMP_STATE_SAVE_YOURSELF_DONE: + /* We need to wait for some response from the server.*/ + process_ice_messages (SmcGetIceConnection (xsmp->connection)); + break; + + default: + /* Hm... shouldn't happen */ + return FALSE; + } + } + + /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and + * the user chooses to save the session. But mate-session will do + * the wrong thing if we pass SmSaveBoth and the user chooses NOT to + * save the session... Sigh. + */ + if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session")) + save_type = SmSaveBoth; + else + save_type = SmSaveGlobal; + + g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : ""); + SmcRequestSaveYourself (xsmp->connection, + save_type, + True, /* shutdown */ + SmInteractStyleAny, + !request_confirmation, /* fast */ + True /* global */); + return TRUE; +} + +static gboolean +idle_do_pending_events (gpointer data) +{ + EggSMClientXSMP *xsmp = data; + EggSMClient *client = data; + + gdk_threads_enter (); + + xsmp->idle = 0; + + if (xsmp->waiting_to_emit_quit) + { + xsmp->waiting_to_emit_quit = FALSE; + egg_sm_client_quit (client); + goto out; + } + + if (xsmp->waiting_to_emit_quit_cancelled) + { + xsmp->waiting_to_emit_quit_cancelled = FALSE; + egg_sm_client_quit_cancelled (client); + xsmp->state = XSMP_STATE_IDLE; + } + + if (xsmp->waiting_to_save_myself) + { + xsmp->waiting_to_save_myself = FALSE; + do_save_yourself (xsmp); + } + + out: + gdk_threads_leave (); + return FALSE; +} + +static void +update_pending_events (EggSMClientXSMP *xsmp) +{ + gboolean want_idle = + xsmp->waiting_to_emit_quit || + xsmp->waiting_to_emit_quit_cancelled || + xsmp->waiting_to_save_myself; + + if (want_idle) + { + if (xsmp->idle == 0) + xsmp->idle = g_idle_add (idle_do_pending_events, xsmp); + } + else + { + if (xsmp->idle != 0) + g_source_remove (xsmp->idle); + xsmp->idle = 0; + } +} + +static void +fix_broken_state (EggSMClientXSMP *xsmp, const char *message, + gboolean send_interact_done, + gboolean send_save_yourself_done) +{ + g_warning ("Received XSMP %s message in state %s: client or server error", + message, EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + /* Forget any pending SaveYourself plans we had */ + xsmp->waiting_to_save_myself = FALSE; + update_pending_events (xsmp); + + if (send_interact_done) + SmcInteractDone (xsmp->connection, False); + if (send_save_yourself_done) + SmcSaveYourselfDone (xsmp->connection, True); + + xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE; +} + +/* SM callbacks */ + +static void +xsmp_save_yourself (SmcConn smc_conn, + SmPointer client_data, + int save_type, + Bool shutdown, + int interact_style, + Bool fast) +{ + EggSMClientXSMP *xsmp = client_data; + gboolean wants_quit_requested; + + g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s", + save_type == SmSaveLocal ? "SmSaveLocal" : + save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth", + shutdown ? "Shutdown" : "!Shutdown", + interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : + interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" : + "SmInteractStyleNone", fast ? "Fast" : "!Fast", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + if (xsmp->state != XSMP_STATE_IDLE && + xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED) + { + fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE); + return; + } + + if (xsmp->waiting_to_set_initial_properties) + sm_client_xsmp_set_initial_properties (xsmp); + + /* If this is the initial SaveYourself, ignore it; we've already set + * properties and there's no reason to actually save state too. + */ + if (xsmp->expecting_initial_save_yourself) + { + xsmp->expecting_initial_save_yourself = FALSE; + + if (save_type == SmSaveLocal && + interact_style == SmInteractStyleNone && + !shutdown && !fast) + { + g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself"); + SmcSaveYourselfDone (xsmp->connection, True); + /* As explained in the comment at the end of + * do_save_yourself(), SAVE_YOURSELF_DONE is the correct + * state here, not IDLE. + */ + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; + return; + } + else + g_warning ("First SaveYourself was not the expected one!"); + } + + /* Even ignoring the "fast" flag completely, there are still 18 + * different combinations of save_type, shutdown and interact_style. + * We interpret them as follows: + * + * Type Shutdown Interact Interpretation + * G F A/E/N do nothing (1) + * G T N do nothing (1)* + * G T A/E quit_requested (2) + * L/B F A/E/N save_state (3) + * L/B T N save_state (3)* + * L/B T A/E quit_requested, then save_state (4) + * + * 1. Do nothing, because the SM asked us to do something + * uninteresting (save open files, but then don't quit + * afterward) or rude (save open files without asking the user + * for confirmation). + * + * 2. Request interaction and then emit ::quit_requested. This + * perhaps isn't quite correct for the SmInteractStyleErrors + * case, but we don't care. + * + * 3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these + * rows essentially get demoted to SmSaveLocal, because their + * Global halves correspond to "do nothing". + * + * 4. Request interaction, emit ::quit_requested, and then emit + * ::save_state after interacting. This is the SmSaveBoth + * equivalent of #2, but we also promote SmSaveLocal shutdown + * SaveYourselfs to SmSaveBoth here, because we want to give + * the user a chance to save open files before quitting. + * + * (* It would be nice if we could do something useful when the + * session manager sends a SaveYourself with shutdown True and + * SmInteractStyleNone. But we can't, so we just pretend it didn't + * even tell us it was shutting down. The docs for ::quit mention + * that it might not always be preceded by ::quit_requested.) + */ + + /* As an optimization, we don't actually request interaction and + * emit ::quit_requested if the application isn't listening to the + * signal. + */ + wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE); + + xsmp->need_save_state = (save_type != SmSaveGlobal); + xsmp->need_quit_requested = (shutdown && wants_quit_requested && + interact_style != SmInteractStyleNone); + xsmp->interact_errors = (interact_style == SmInteractStyleErrors); + + xsmp->shutting_down = shutdown; + + do_save_yourself (xsmp); +} + +static void +do_save_yourself (EggSMClientXSMP *xsmp) +{ + if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) + { + /* The SM cancelled a previous SaveYourself, but we haven't yet + * had a chance to tell the application, so we can't start + * processing this SaveYourself yet. + */ + xsmp->waiting_to_save_myself = TRUE; + update_pending_events (xsmp); + return; + } + + if (xsmp->need_quit_requested) + { + xsmp->state = XSMP_STATE_INTERACT_REQUEST; + + g_debug ("Sending InteractRequest(%s)", + xsmp->interact_errors ? "Error" : "Normal"); + SmcInteractRequest (xsmp->connection, + xsmp->interact_errors ? SmDialogError : SmDialogNormal, + xsmp_interact, + xsmp); + return; + } + + if (xsmp->need_save_state) + { + save_state (xsmp); + + /* Though unlikely, the client could have been disconnected + * while the application was saving its state. + */ + if (!xsmp->connection) + return; + } + + g_debug ("Sending SaveYourselfDone(True)"); + SmcSaveYourselfDone (xsmp->connection, True); + + /* The client state diagram in the XSMP spec says that after a + * non-shutdown SaveYourself, we go directly back to "idle". But + * everything else in both the XSMP spec and the libSM docs + * disagrees. + */ + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; +} + +static void +save_state (EggSMClientXSMP *xsmp) +{ + GKeyFile *state_file; + char *state_file_path, *data; + EggDesktopFile *desktop_file; + GPtrArray *restart, *discard; + int offset, fd; + + /* We set xsmp->state before emitting save_state, but our caller is + * responsible for setting it back afterward. + */ + xsmp->state = XSMP_STATE_SAVE_YOURSELF; + + state_file = egg_sm_client_save_state ((EggSMClient *)xsmp); + if (!state_file) + { + restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); + set_properties (xsmp, + ptrarray_prop (SmRestartCommand, restart), + NULL); + g_ptr_array_free (restart, TRUE); + + if (xsmp->set_discard_command) + { + discard = generate_command (xsmp->discard_command, NULL, NULL); + set_properties (xsmp, + ptrarray_prop (SmDiscardCommand, discard), + NULL); + g_ptr_array_free (discard, TRUE); + } + else + delete_properties (xsmp, SmDiscardCommand, NULL); + + return; + } + + desktop_file = egg_get_desktop_file (); + if (desktop_file) + { + GKeyFile *merged_file; + char *desktop_file_path; + + merged_file = g_key_file_new (); + desktop_file_path = + g_filename_from_uri (egg_desktop_file_get_source (desktop_file), + NULL, NULL); + if (desktop_file_path && + g_key_file_load_from_file (merged_file, desktop_file_path, + G_KEY_FILE_KEEP_COMMENTS | + G_KEY_FILE_KEEP_TRANSLATIONS, NULL)) + { + guint g, k, i; + char **groups, **keys, *value, *exec; + + groups = g_key_file_get_groups (state_file, NULL); + for (g = 0; groups[g]; g++) + { + keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL); + for (k = 0; keys[k]; k++) + { + value = g_key_file_get_value (state_file, groups[g], + keys[k], NULL); + if (value) + { + g_key_file_set_value (merged_file, groups[g], + keys[k], value); + g_free (value); + } + } + g_strfreev (keys); + } + g_strfreev (groups); + + g_key_file_free (state_file); + state_file = merged_file; + + /* Update Exec key using "--sm-client-state-file %k" */ + restart = generate_command (xsmp->restart_command, + NULL, "%k"); + for (i = 0; i < restart->len; i++) + restart->pdata[i] = g_shell_quote (restart->pdata[i]); + g_ptr_array_add (restart, NULL); + exec = g_strjoinv (" ", (char **)restart->pdata); + g_strfreev ((char **)restart->pdata); + g_ptr_array_free (restart, FALSE); + + g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_EXEC, + exec); + g_free (exec); + } + else + desktop_file = NULL; + + g_free (desktop_file_path); + } + + /* Now write state_file to disk. (We can't use mktemp(), because + * that requires the filename to end with "XXXXXX", and we want + * it to end with ".desktop".) + */ + + data = g_key_file_to_data (state_file, NULL, NULL); + g_key_file_free (state_file); + + offset = 0; + while (1) + { + state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s", + g_get_user_config_dir (), + G_DIR_SEPARATOR, G_DIR_SEPARATOR, + g_get_prgname (), + (long)time (NULL) + offset, + desktop_file ? "desktop" : "state"); + + fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (fd == -1) + { + if (errno == EEXIST) + { + offset++; + g_free (state_file_path); + continue; + } + else if (errno == ENOTDIR || errno == ENOENT) + { + char *sep = strrchr (state_file_path, G_DIR_SEPARATOR); + + *sep = '\0'; + if (g_mkdir_with_parents (state_file_path, 0755) != 0) + { + g_warning ("Could not create directory '%s'", + state_file_path); + g_free (state_file_path); + state_file_path = NULL; + break; + } + + continue; + } + + g_warning ("Could not create file '%s': %s", + state_file_path, g_strerror (errno)); + g_free (state_file_path); + state_file_path = NULL; + break; + } + + close (fd); + g_file_set_contents (state_file_path, data, -1, NULL); + break; + } + g_free (data); + + restart = generate_command (xsmp->restart_command, xsmp->client_id, + state_file_path); + set_properties (xsmp, + ptrarray_prop (SmRestartCommand, restart), + NULL); + g_ptr_array_free (restart, TRUE); + + if (state_file_path) + { + set_properties (xsmp, + array_prop (SmDiscardCommand, + "/bin/rm", "-rf", state_file_path, + NULL), + NULL); + g_free (state_file_path); + } +} + +static void +xsmp_interact (SmcConn smc_conn, + SmPointer client_data) +{ + EggSMClientXSMP *xsmp = client_data; + EggSMClient *client = client_data; + + g_debug ("Received Interact message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + if (xsmp->state != XSMP_STATE_INTERACT_REQUEST) + { + fix_broken_state (xsmp, "Interact", TRUE, TRUE); + return; + } + + xsmp->state = XSMP_STATE_INTERACT; + egg_sm_client_quit_requested (client); +} + +static void +xsmp_die (SmcConn smc_conn, + SmPointer client_data) +{ + EggSMClientXSMP *xsmp = client_data; + EggSMClient *client = client_data; + + g_debug ("Received Die message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + sm_client_xsmp_disconnect (xsmp); + egg_sm_client_quit (client); +} + +static void +xsmp_save_complete (SmcConn smc_conn, + SmPointer client_data) +{ + EggSMClientXSMP *xsmp = client_data; + + g_debug ("Received SaveComplete message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) + xsmp->state = XSMP_STATE_IDLE; + else + fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE); +} + +static void +xsmp_shutdown_cancelled (SmcConn smc_conn, + SmPointer client_data) +{ + EggSMClientXSMP *xsmp = client_data; + EggSMClient *client = client_data; + + g_debug ("Received ShutdownCancelled message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + xsmp->shutting_down = FALSE; + + if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) + { + /* We've finished interacting and now the SM has agreed to + * cancel the shutdown. + */ + xsmp->state = XSMP_STATE_IDLE; + egg_sm_client_quit_cancelled (client); + } + else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) + { + /* Hm... ok, so we got a shutdown SaveYourself, which got + * cancelled, but the application was still interacting, so we + * didn't tell it yet, and then *another* SaveYourself arrived, + * which we must still be waiting to tell the app about, except + * that now that SaveYourself has been cancelled too! Dizzy yet? + */ + xsmp->waiting_to_save_myself = FALSE; + update_pending_events (xsmp); + } + else + { + g_debug ("Sending SaveYourselfDone(False)"); + SmcSaveYourselfDone (xsmp->connection, False); + + if (xsmp->state == XSMP_STATE_INTERACT) + { + /* The application is currently interacting, so we can't + * tell it about the cancellation yet; we will wait until + * after it calls egg_sm_client_will_quit(). + */ + xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED; + } + else + { + /* The shutdown was cancelled before the application got a + * chance to interact. + */ + xsmp->state = XSMP_STATE_IDLE; + } + } +} + +/* Utilities */ + +/* Create a restart/clone/Exec command based on @restart_command. + * If @client_id is non-%NULL, add "--sm-client-id @client_id". + * If @state_file is non-%NULL, add "--sm-client-state-file @state_file". + * + * None of the input strings are g_strdup()ed; the caller must keep + * them around until it is done with the returned GPtrArray, and must + * then free the array, but not its contents. + */ +static GPtrArray * +generate_command (char **argv, const char *client_id, + const char *state_file) +{ + GPtrArray *cmd; + int i; + + cmd = g_ptr_array_new (); + g_ptr_array_add (cmd, argv[0]); + + if (client_id) + { + g_ptr_array_add (cmd, (char *)"--sm-client-id"); + g_ptr_array_add (cmd, (char *)client_id); + } + + if (state_file) + { + g_ptr_array_add (cmd, (char *)"--sm-client-state-file"); + g_ptr_array_add (cmd, (char *)state_file); + } + + for (i = 1; argv[i]; i++) + g_ptr_array_add (cmd, argv[i]); + + return cmd; +} + +/* Takes a NULL-terminated list of SmProp * values, created by + * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and + * frees them. + */ +static void +set_properties (EggSMClientXSMP *xsmp, ...) +{ + GPtrArray *props; + SmProp *prop; + va_list ap; + guint i; + + props = g_ptr_array_new (); + + va_start (ap, xsmp); + while ((prop = va_arg (ap, SmProp *))) + g_ptr_array_add (props, prop); + va_end (ap); + + if (xsmp->connection) + { + SmcSetProperties (xsmp->connection, props->len, + (SmProp **)props->pdata); + } + + for (i = 0; i < props->len; i++) + { + prop = props->pdata[i]; + g_free (prop->vals); + g_free (prop); + } + g_ptr_array_free (props, TRUE); +} + +/* Takes a NULL-terminated list of property names and deletes them. */ +static void +delete_properties (EggSMClientXSMP *xsmp, ...) +{ + GPtrArray *props; + char *prop; + va_list ap; + + if (!xsmp->connection) + return; + + props = g_ptr_array_new (); + + va_start (ap, xsmp); + while ((prop = va_arg (ap, char *))) + g_ptr_array_add (props, prop); + va_end (ap); + + SmcDeleteProperties (xsmp->connection, props->len, + (char **)props->pdata); + + g_ptr_array_free (props, TRUE); +} + +/* Takes an array of strings and creates a LISTofARRAY8 property. The + * strings are neither dupped nor freed; they need to remain valid + * until you're done with the SmProp. + */ +static SmProp * +array_prop (const char *name, ...) +{ + SmProp *prop; + SmPropValue pv; + GArray *vals; + char *value; + va_list ap; + + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = (char *)SmLISTofARRAY8; + + vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); + + va_start (ap, name); + while ((value = va_arg (ap, char *))) + { + pv.length = strlen (value); + pv.value = value; + g_array_append_val (vals, pv); + } + + prop->num_vals = vals->len; + prop->vals = (SmPropValue *)vals->data; + + g_array_free (vals, FALSE); + + return prop; +} + +/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property. + * The array contents are neither dupped nor freed; they need to + * remain valid until you're done with the SmProp. + */ +static SmProp * +ptrarray_prop (const char *name, GPtrArray *values) +{ + SmProp *prop; + SmPropValue pv; + GArray *vals; + guint i; + + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = (char *)SmLISTofARRAY8; + + vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); + + for (i = 0; i < values->len; i++) + { + pv.length = strlen (values->pdata[i]); + pv.value = values->pdata[i]; + g_array_append_val (vals, pv); + } + + prop->num_vals = vals->len; + prop->vals = (SmPropValue *)vals->data; + + g_array_free (vals, FALSE); + + return prop; +} + +/* Takes a string and creates an ARRAY8 property. The string is + * neither dupped nor freed; it needs to remain valid until you're + * done with the SmProp. + */ +static SmProp * +string_prop (const char *name, const char *value) +{ + SmProp *prop; + + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = (char *)SmARRAY8; + + prop->num_vals = 1; + prop->vals = g_new (SmPropValue, 1); + + prop->vals[0].length = strlen (value); + prop->vals[0].value = (char *)value; + + return prop; +} + +/* Takes a char and creates a CARD8 property. */ +static SmProp * +card8_prop (const char *name, unsigned char value) +{ + SmProp *prop; + char *card8val; + + /* To avoid having to allocate and free prop->vals[0], we cheat and + * make vals a 2-element-long array and then use the second element + * to store value. + */ + + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = (char *)SmCARD8; + + prop->num_vals = 1; + prop->vals = g_new (SmPropValue, 2); + card8val = (char *)(&prop->vals[1]); + card8val[0] = value; + + prop->vals[0].length = 1; + prop->vals[0].value = card8val; + + return prop; +} + +/* ICE code. This makes no effort to play nice with anyone else trying + * to use libICE. Fortunately, no one uses libICE for anything other + * than SM. (DCOP uses ICE, but it has its own private copy of + * libICE.) + * + * When this moves to gtk, it will need to be cleverer, to avoid + * tripping over old apps that use MateClient or that use libSM + * directly. + */ + +#include <X11/ICE/ICElib.h> +#include <fcntl.h> + +static void ice_error_handler (IceConn ice_conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + IcePointer values); +static void ice_io_error_handler (IceConn ice_conn); +static void ice_connection_watch (IceConn ice_conn, + IcePointer client_data, + Bool opening, + IcePointer *watch_data); + +static void +ice_init (void) +{ + IceSetIOErrorHandler (ice_io_error_handler); + IceSetErrorHandler (ice_error_handler); + IceAddConnectionWatch (ice_connection_watch, NULL); +} + +static gboolean +process_ice_messages (IceConn ice_conn) +{ + IceProcessMessagesStatus status; + + gdk_threads_enter (); + status = IceProcessMessages (ice_conn, NULL, NULL); + gdk_threads_leave (); + + switch (status) + { + case IceProcessMessagesSuccess: + return TRUE; + + case IceProcessMessagesIOError: + sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn)); + return FALSE; + + case IceProcessMessagesConnectionClosed: + return FALSE; + + default: + g_assert_not_reached (); + } +} + +static gboolean +ice_iochannel_watch (GIOChannel *channel, + GIOCondition condition, + gpointer client_data) +{ + return process_ice_messages (client_data); +} + +static void +ice_connection_watch (IceConn ice_conn, + IcePointer client_data, + Bool opening, + IcePointer *watch_data) +{ + guint watch_id; + + if (opening) + { + GIOChannel *channel; + int fd = IceConnectionNumber (ice_conn); + + fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC); + channel = g_io_channel_unix_new (fd); + watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR, + ice_iochannel_watch, ice_conn); + g_io_channel_unref (channel); + + *watch_data = GUINT_TO_POINTER (watch_id); + } + else + { + watch_id = GPOINTER_TO_UINT (*watch_data); + g_source_remove (watch_id); + } +} + +static void +ice_error_handler (IceConn ice_conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + IcePointer values) +{ + /* Do nothing */ +} + +static void +ice_io_error_handler (IceConn ice_conn) +{ + /* Do nothing */ +} + +static void +smc_error_handler (SmcConn smc_conn, + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + SmPointer values) +{ + /* Do nothing */ +} diff --git a/src/eggsmclient.c b/src/eggsmclient.c new file mode 100644 index 0000000..31906b6 --- /dev/null +++ b/src/eggsmclient.c @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <string.h> +#include <glib/gi18n.h> + +#include "eggsmclient.h" +#include "eggsmclient-private.h" + +static void egg_sm_client_debug_handler (const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer user_data); + +enum { + SAVE_STATE, + QUIT_REQUESTED, + QUIT_CANCELLED, + QUIT, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +struct _EggSMClientPrivate { + GKeyFile *state_file; +}; + +#define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, EggSMClientPrivate)) + +G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT) + +static EggSMClient *global_client; +static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL; + +static void +egg_sm_client_init (EggSMClient *client) +{ + ; +} + +static void +egg_sm_client_class_init (EggSMClientClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (EggSMClientPrivate)); + + /** + * EggSMClient::save_state: + * @client: the client + * @state_file: a #GKeyFile to save state information into + * + * Emitted when the session manager has requested that the + * application save information about its current state. The + * application should save its state into @state_file, and then the + * session manager may then restart the application in a future + * session and tell it to initialize itself from that state. + * + * You should not save any data into @state_file's "start group" + * (ie, the %NULL group). Instead, applications should save their + * data into groups with names that start with the application name, + * and libraries that connect to this signal should save their data + * into groups with names that start with the library name. + * + * Alternatively, rather than (or in addition to) using @state_file, + * the application can save its state by calling + * egg_sm_client_set_restart_command() during the processing of this + * signal (eg, to include a list of files to open). + **/ + signals[SAVE_STATE] = + g_signal_new ("save_state", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggSMClientClass, save_state), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, G_TYPE_POINTER); + + /** + * EggSMClient::quit_requested: + * @client: the client + * + * Emitted when the session manager requests that the application + * exit (generally because the user is logging out). The application + * should decide whether or not it is willing to quit (perhaps after + * asking the user what to do with documents that have unsaved + * changes) and then call egg_sm_client_will_quit(), passing %TRUE + * or %FALSE to give its answer to the session manager. (It does not + * need to give an answer before returning from the signal handler; + * it can interact with the user asynchronously and then give its + * answer later on.) If the application does not connect to this + * signal, then #EggSMClient will automatically return %TRUE on its + * behalf. + * + * The application should not save its session state as part of + * handling this signal; if the user has requested that the session + * be saved when logging out, then ::save_state will be emitted + * separately. + * + * If the application agrees to quit, it should then wait for either + * the ::quit_cancelled or ::quit signals to be emitted. + **/ + signals[QUIT_REQUESTED] = + g_signal_new ("quit_requested", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggSMClientClass, quit_requested), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + /** + * EggSMClient::quit_cancelled: + * @client: the client + * + * Emitted when the session manager decides to cancel a logout after + * the application has already agreed to quit. After receiving this + * signal, the application can go back to what it was doing before + * receiving the ::quit_requested signal. + **/ + signals[QUIT_CANCELLED] = + g_signal_new ("quit_cancelled", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + /** + * EggSMClient::quit: + * @client: the client + * + * Emitted when the session manager wants the application to quit + * (generally because the user is logging out). The application + * should exit as soon as possible after receiving this signal; if + * it does not, the session manager may choose to forcibly kill it. + * + * Normally a GUI application would only be sent a ::quit if it + * agreed to quit in response to a ::quit_requested signal. However, + * this is not guaranteed; in some situations the session manager + * may decide to end the session without giving applications a + * chance to object. + **/ + signals[QUIT] = + g_signal_new ("quit", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggSMClientClass, quit), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +static gboolean sm_client_disable = FALSE; +static char *sm_client_state_file = NULL; +static char *sm_client_id = NULL; +static char *sm_config_prefix = NULL; + +static gboolean +sm_client_post_parse_func (GOptionContext *context, + GOptionGroup *group, + gpointer data, + GError **error) +{ + EggSMClient *client = egg_sm_client_get (); + + if (sm_client_id == NULL) + { + const gchar *desktop_autostart_id; + + desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); + + if (desktop_autostart_id != NULL) + sm_client_id = g_strdup (desktop_autostart_id); + } + + /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to + * use the same client id. */ + g_unsetenv ("DESKTOP_AUTOSTART_ID"); + + if (EGG_SM_CLIENT_GET_CLASS (client)->startup) + EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id); + return TRUE; +} + +/** + * egg_sm_client_get_option_group: + * + * Creates a %GOptionGroup containing the session-management-related + * options. You should add this group to the application's + * %GOptionContext if you want to use #EggSMClient. + * + * Return value: the %GOptionGroup + **/ +GOptionGroup * +egg_sm_client_get_option_group (void) +{ + const GOptionEntry entries[] = { + { "sm-client-disable", 0, 0, + G_OPTION_ARG_NONE, &sm_client_disable, + N_("Disable connection to session manager"), NULL }, + { "sm-client-state-file", 0, 0, + G_OPTION_ARG_FILENAME, &sm_client_state_file, + N_("Specify file containing saved configuration"), N_("FILE") }, + { "sm-client-id", 0, 0, + G_OPTION_ARG_STRING, &sm_client_id, + N_("Specify session management ID"), N_("ID") }, + /* MateClient compatibility option */ + { "sm-disable", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, &sm_client_disable, + NULL, NULL }, + /* MateClient compatibility option. This is a dummy option that only + * exists so that sessions saved by apps with MateClient can be restored + * later when they've switched to EggSMClient. See bug #575308. + */ + { "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_STRING, &sm_config_prefix, + NULL, NULL }, + { NULL } + }; + GOptionGroup *group; + + /* Use our own debug handler for the "EggSMClient" domain. */ + g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + egg_sm_client_debug_handler, NULL); + + group = g_option_group_new ("sm-client", + _("Session management options:"), + _("Show session management options"), + NULL, NULL); + g_option_group_add_entries (group, entries); + g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func); + + return group; +} + +/** + * egg_sm_client_set_mode: + * @mode: an #EggSMClient mode + * + * Sets the "mode" of #EggSMClient as follows: + * + * %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely + * disabled. The application will not even connect to the session + * manager. (egg_sm_client_get() will still return an #EggSMClient, + * but it will just be a dummy object.) + * + * %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to + * the session manager (and thus will receive notification when the + * user is logging out, etc), but will request to not be + * automatically restarted with saved state in future sessions. + * + * %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will + * function normally. + * + * This must be called before the application's main loop begins. + **/ +void +egg_sm_client_set_mode (EggSMClientMode mode) +{ + global_client_mode = mode; +} + +/** + * egg_sm_client_get_mode: + * + * Gets the global #EggSMClientMode. See egg_sm_client_set_mode() + * for details. + * + * Return value: the global #EggSMClientMode + **/ +EggSMClientMode +egg_sm_client_get_mode (void) +{ + return global_client_mode; +} + +/** + * egg_sm_client_get: + * + * Returns the master #EggSMClient for the application. + * + * On platforms that support saved sessions (ie, POSIX/X11), the + * application will only request to be restarted by the session + * manager if you call egg_set_desktop_file() to set an application + * desktop file. In particular, if the desktop file contains the key + * "X + * + * Return value: the master #EggSMClient. + **/ +EggSMClient * +egg_sm_client_get (void) +{ + if (!global_client) + { + if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED && + !sm_client_disable) + { +#if defined (GDK_WINDOWING_WIN32) + global_client = egg_sm_client_win32_new (); +#elif defined (GDK_WINDOWING_QUARTZ) + global_client = egg_sm_client_osx_new (); +#else + /* If both D-Bus and XSMP are compiled in, try XSMP first + * (since it supports state saving) and fall back to D-Bus + * if XSMP isn't available. + */ +# ifdef EGG_SM_CLIENT_BACKEND_XSMP + global_client = egg_sm_client_xsmp_new (); +# endif +# ifdef EGG_SM_CLIENT_BACKEND_DBUS + if (!global_client) + global_client = egg_sm_client_dbus_new (); +# endif +#endif + } + + /* Fallback: create a dummy client, so that callers don't have + * to worry about a %NULL return value. + */ + if (!global_client) + global_client = g_object_new (EGG_TYPE_SM_CLIENT, NULL); + } + + return global_client; +} + +/** + * egg_sm_client_is_resumed: + * @client: the client + * + * Checks whether or not the current session has been resumed from + * a previous saved session. If so, the application should call + * egg_sm_client_get_state_file() and restore its state from the + * returned #GKeyFile. + * + * Return value: %TRUE if the session has been resumed + **/ +gboolean +egg_sm_client_is_resumed (EggSMClient *client) +{ + g_return_val_if_fail (client == global_client, FALSE); + + return sm_client_state_file != NULL; +} + +/** + * egg_sm_client_get_state_file: + * @client: the client + * + * If the application was resumed by the session manager, this will + * return the #GKeyFile containing its state from the previous + * session. + * + * Note that other libraries and #EggSMClient itself may also store + * state in the key file, so if you call egg_sm_client_get_groups(), + * on it, the return value will likely include groups that you did not + * put there yourself. (It is also not guaranteed that the first + * group created by the application will still be the "start group" + * when it is resumed.) + * + * Return value: the #GKeyFile containing the application's earlier + * state, or %NULL on error. You should not free this key file; it + * is owned by @client. + **/ +GKeyFile * +egg_sm_client_get_state_file (EggSMClient *client) +{ + EggSMClientPrivate *priv = EGG_SM_CLIENT_GET_PRIVATE (client); + char *state_file_path; + GError *err = NULL; + + g_return_val_if_fail (client == global_client, NULL); + + if (!sm_client_state_file) + return NULL; + if (priv->state_file) + return priv->state_file; + + if (!strncmp (sm_client_state_file, "file://", 7)) + state_file_path = g_filename_from_uri (sm_client_state_file, NULL, NULL); + else + state_file_path = g_strdup (sm_client_state_file); + + priv->state_file = g_key_file_new (); + if (!g_key_file_load_from_file (priv->state_file, state_file_path, 0, &err)) + { + g_warning ("Could not load SM state file '%s': %s", + sm_client_state_file, err->message); + g_clear_error (&err); + g_key_file_free (priv->state_file); + priv->state_file = NULL; + } + + g_free (state_file_path); + return priv->state_file; +} + +/** + * egg_sm_client_set_restart_command: + * @client: the client + * @argc: the length of @argv + * @argv: argument vector + * + * Sets the command used to restart @client if it does not have a + * .desktop file that can be used to find its restart command. + * + * This can also be used when handling the ::save_state signal, to + * save the current state via an updated command line. (Eg, providing + * a list of filenames to open when the application is resumed.) + **/ +void +egg_sm_client_set_restart_command (EggSMClient *client, + int argc, + const char **argv) +{ + g_return_if_fail (EGG_IS_SM_CLIENT (client)); + + if (EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command) + EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command (client, argc, argv); +} + +/** + * egg_sm_client_set_discard_command: + * @client: the client + * @argc: the length of @argv + * @argv: argument vector + * + * Sets the command used to discard a custom state file if using + * egg_sm_client_set_restart_command(), which must be called before + * using this function. + **/ +void +egg_sm_client_set_discard_command (EggSMClient *client, + int argc, + const char **argv) +{ + g_return_if_fail (EGG_IS_SM_CLIENT (client)); + + if (EGG_SM_CLIENT_GET_CLASS (client)->set_discard_command) + EGG_SM_CLIENT_GET_CLASS (client)->set_discard_command (client, argc, argv); +} + +/** + * egg_sm_client_will_quit: + * @client: the client + * @will_quit: whether or not the application is willing to quit + * + * This MUST be called in response to the ::quit_requested signal, to + * indicate whether or not the application is willing to quit. The + * application may call it either directly from the signal handler, or + * at some later point (eg, after asynchronously interacting with the + * user). + * + * If the application does not connect to ::quit_requested, + * #EggSMClient will call this method on its behalf (passing %TRUE + * for @will_quit). + * + * After calling this method, the application should wait to receive + * either ::quit_cancelled or ::quit. + **/ +void +egg_sm_client_will_quit (EggSMClient *client, + gboolean will_quit) +{ + g_return_if_fail (EGG_IS_SM_CLIENT (client)); + + if (EGG_SM_CLIENT_GET_CLASS (client)->will_quit) + EGG_SM_CLIENT_GET_CLASS (client)->will_quit (client, will_quit); +} + +/** + * egg_sm_client_end_session: + * @style: a hint at how to end the session + * @request_confirmation: whether or not the user should get a chance + * to confirm the action + * + * Requests that the session manager end the current session. @style + * indicates how the session should be ended, and + * @request_confirmation indicates whether or not the user should be + * given a chance to confirm the logout/reboot/shutdown. Both of these + * flags are merely hints though; the session manager may choose to + * ignore them. + * + * Return value: %TRUE if the request was sent; %FALSE if it could not + * be (eg, because it could not connect to the session manager). + **/ +gboolean +egg_sm_client_end_session (EggSMClientEndStyle style, + gboolean request_confirmation) +{ + EggSMClient *client = egg_sm_client_get (); + + g_return_val_if_fail (EGG_IS_SM_CLIENT (client), FALSE); + + if (EGG_SM_CLIENT_GET_CLASS (client)->end_session) + { + return EGG_SM_CLIENT_GET_CLASS (client)->end_session (client, style, + request_confirmation); + } + else + return FALSE; +} + +/* Signal-emitting callbacks from platform-specific code */ + +GKeyFile * +egg_sm_client_save_state (EggSMClient *client) +{ + GKeyFile *state_file; + char *group; + + g_return_val_if_fail (client == global_client, NULL); + + state_file = g_key_file_new (); + + g_debug ("Emitting save_state"); + g_signal_emit (client, signals[SAVE_STATE], 0, state_file); + g_debug ("Done emitting save_state"); + + group = g_key_file_get_start_group (state_file); + if (group) + { + g_free (group); + return state_file; + } + else + { + g_key_file_free (state_file); + return NULL; + } +} + +void +egg_sm_client_quit_requested (EggSMClient *client) +{ + g_return_if_fail (client == global_client); + + if (!g_signal_has_handler_pending (client, signals[QUIT_REQUESTED], 0, FALSE)) + { + g_debug ("Not emitting quit_requested because no one is listening"); + egg_sm_client_will_quit (client, TRUE); + return; + } + + g_debug ("Emitting quit_requested"); + g_signal_emit (client, signals[QUIT_REQUESTED], 0); + g_debug ("Done emitting quit_requested"); +} + +void +egg_sm_client_quit_cancelled (EggSMClient *client) +{ + g_return_if_fail (client == global_client); + + g_debug ("Emitting quit_cancelled"); + g_signal_emit (client, signals[QUIT_CANCELLED], 0); + g_debug ("Done emitting quit_cancelled"); +} + +void +egg_sm_client_quit (EggSMClient *client) +{ + g_return_if_fail (client == global_client); + + g_debug ("Emitting quit"); + g_signal_emit (client, signals[QUIT], 0); + g_debug ("Done emitting quit"); + + /* FIXME: should we just call gtk_main_quit() here? */ +} + +static void +egg_sm_client_debug_handler (const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer user_data) +{ + static int debug = -1; + + if (debug < 0) + debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL); + + if (debug) + g_log_default_handler (log_domain, log_level, message, NULL); +} diff --git a/src/eggsmclient.h b/src/eggsmclient.h new file mode 100644 index 0000000..6de47b6 --- /dev/null +++ b/src/eggsmclient.h @@ -0,0 +1,123 @@ +/* eggsmclient.h + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_SM_CLIENT_H__ +#define __EGG_SM_CLIENT_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define EGG_TYPE_SM_CLIENT (egg_sm_client_get_type ()) +#define EGG_SM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT, EggSMClient)) +#define EGG_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT, EggSMClientClass)) +#define EGG_IS_SM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT)) +#define EGG_IS_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT)) +#define EGG_SM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT, EggSMClientClass)) + +typedef struct _EggSMClient EggSMClient; +typedef struct _EggSMClientClass EggSMClientClass; +typedef struct _EggSMClientPrivate EggSMClientPrivate; + +typedef enum { + EGG_SM_CLIENT_END_SESSION_DEFAULT, + EGG_SM_CLIENT_LOGOUT, + EGG_SM_CLIENT_REBOOT, + EGG_SM_CLIENT_SHUTDOWN +} EggSMClientEndStyle; + +typedef enum { + EGG_SM_CLIENT_MODE_DISABLED, + EGG_SM_CLIENT_MODE_NO_RESTART, + EGG_SM_CLIENT_MODE_NORMAL +} EggSMClientMode; + +struct _EggSMClient +{ + GObject parent; + +}; + +struct _EggSMClientClass +{ + GObjectClass parent_class; + + /* signals */ + void (*save_state) (EggSMClient *client, + GKeyFile *state_file); + + void (*quit_requested) (EggSMClient *client); + void (*quit_cancelled) (EggSMClient *client); + void (*quit) (EggSMClient *client); + + /* virtual methods */ + void (*startup) (EggSMClient *client, + const char *client_id); + void (*set_restart_command) (EggSMClient *client, + int argc, + const char **argv); + void (*set_discard_command) (EggSMClient *client, + int argc, + const char **argv); + void (*will_quit) (EggSMClient *client, + gboolean will_quit); + gboolean (*end_session) (EggSMClient *client, + EggSMClientEndStyle style, + gboolean request_confirmation); + + /* Padding for future expansion */ + void (*_egg_reserved1) (void); + void (*_egg_reserved2) (void); + void (*_egg_reserved3) (void); + void (*_egg_reserved4) (void); +}; + +GType egg_sm_client_get_type (void) G_GNUC_CONST; + +GOptionGroup *egg_sm_client_get_option_group (void); + +/* Initialization */ +void egg_sm_client_set_mode (EggSMClientMode mode); +EggSMClientMode egg_sm_client_get_mode (void); +EggSMClient *egg_sm_client_get (void); + +/* Resuming a saved session */ +gboolean egg_sm_client_is_resumed (EggSMClient *client); +GKeyFile *egg_sm_client_get_state_file (EggSMClient *client); + +/* Alternate means of saving state */ +void egg_sm_client_set_restart_command (EggSMClient *client, + int argc, + const char **argv); +void egg_sm_client_set_discard_command (EggSMClient *client, + int argc, + const char **argv); + +/* Handling "quit_requested" signal */ +void egg_sm_client_will_quit (EggSMClient *client, + gboolean will_quit); + +/* Initiate a logout/reboot/shutdown */ +gboolean egg_sm_client_end_session (EggSMClientEndStyle style, + gboolean request_confirmation); + +G_END_DECLS + + +#endif /* __EGG_SM_CLIENT_H__ */ diff --git a/src/encodings-dialog.glade b/src/encodings-dialog.glade new file mode 100644 index 0000000..f86a0f0 --- /dev/null +++ b/src/encodings-dialog.glade @@ -0,0 +1,296 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.mate.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="encodings-dialog"> + <property name="border_width">5</property> + <property name="title" translatable="yes">Add or Remove Terminal Encodings</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="default_height">325</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox3"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area3"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="helpbutton1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-help</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-11</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="closebutton1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-7</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="table33"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="available-treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + <accessibility> + <atkrelation target="available-label" type="labelled-by"/> + </accessibility> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox27"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkVBox" id="vbox87"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkButton" id="add-button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkImage" id="image3"> + <property name="visible">True</property> + <property name="stock">gtk-go-forward</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="remove-button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkImage" id="image4"> + <property name="visible">True</property> + <property name="stock">gtk-go-back</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="available-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">A_vailable encodings:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">available-treeview</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + <accessibility> + <atkrelation target="available-treeview" type="label-for"/> + </accessibility> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="displayed-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">E_ncodings shown in menu:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">displayed-treeview</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow3"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="displayed-treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/src/extra-strings.c b/src/extra-strings.c new file mode 100644 index 0000000..0a24c60 --- /dev/null +++ b/src/extra-strings.c @@ -0,0 +1,73 @@ +/* + * Copyright © 2009 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +/* This file contains extra strings that need to be translated, but + * can't be extracted by intltool since the ui files aren't in git, and + * the glade files don't contain them in the right form. See bug #553357. + */ + +/* Translators: This refers to the Delete keybinding option */ +N_("Automatic") +/* Translators: This refers to the Delete keybinding option */ +N_("Control-H") +/* Translators: This refers to the Delete keybinding option */ +N_("ASCII DEL") +/* Translators: This refers to the Delete keybinding option */ +N_("Escape sequence") +/* Translators: This refers to the Delete keybinding option */ +N_("TTY Erase") + +/* Translators: Cursor shape: ... */ +N_("Block") +/* Translators: Cursor shape: ... */ +N_("I-Beam") +/* Translators: Cursor shape: ... */ +N_("Underline") + +/* Translators: When command exits: ... */ +N_("Exit the terminal") +/* Translators: When command exits: ... */ +N_("Restart the command") +/* Translators: When command exits: ... */ +N_("Hold the terminal open") + +/* Translators: Scrollbar is: ... */ +N_("On the left side") +/* Translators: Scrollbar is: ... */ +N_("On the right side") +/* Translators: Scrollbar is: ... */ +N_("Disabled") + +/* Translators: When terminal commands set their own titles: ... */ +N_("Replace initial title") +/* Translators: When terminal commands set their own titles: ... */ +N_("Append initial title") +/* Translators: When terminal commands set their own titles: ... */ +N_("Prepend initial title") +/* Translators: When terminal commands set their own titles: ... */ +N_("Keep initial title") + +/* Translators: This is the name of a colour scheme */ +N_("Tango") +/* Translators: This is the name of a colour scheme */ +N_("Linux console") +/* Translators: This is the name of a colour scheme */ +N_("XTerm") +/* Translators: This is the name of a colour scheme */ +N_("Rxvt") +/* Translators: This is the name of a colour scheme */ +N_("Custom") diff --git a/src/find-dialog.glade b/src/find-dialog.glade new file mode 100644 index 0000000..71f8b78 --- /dev/null +++ b/src/find-dialog.glade @@ -0,0 +1,236 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.mate.org/glade-2.0.dtd"> + +<glade-interface> +<requires lib="mate"/> + +<widget class="GtkDialog" id="find-dialog"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="title" translatable="yes">Find</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">True</property> + <property name="icon_name">gtk-find</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="hbuttonbox2"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="button-close"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">False</property> + <property name="response_id">-7</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button-find"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-find</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-3</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox2"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="search-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Search for:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">search-entry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBoxEntry" id="search-entry"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="has_frame">True</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="match-case-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Match case</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">False</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="entire-word-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Match _entire word only</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">False</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="regex-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Match as _regular expression</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">False</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="search-backwards-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Search _backwards</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">False</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="wrap-around-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Wrap around</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">False</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/src/keybinding-editor.glade b/src/keybinding-editor.glade new file mode 100644 index 0000000..bdc121f --- /dev/null +++ b/src/keybinding-editor.glade @@ -0,0 +1,194 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.mate.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="keybindings-dialog"> + <property name="border_width">5</property> + <property name="title" translatable="yes">Keyboard Shortcuts</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="role">mate-terminal-accels</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox2"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area2"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="close_button1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-7</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="button1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-help</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-11</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="table32"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="n_rows">4</property> + <property name="n_columns">1</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkCheckButton" id="disable-mnemonics-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Enable menu access keys (such as Alt+F to open the File menu)</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="accelerators-treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">True</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="disable-menu-accel-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Enable the _menu shortcut key (F10 by default)</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label59"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Shortcut keys:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">accelerators-treeview</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/src/mate-terminal.schemas.in b/src/mate-terminal.schemas.in new file mode 100644 index 0000000..5eb5573 --- /dev/null +++ b/src/mate-terminal.schemas.in @@ -0,0 +1,1302 @@ +<mateconfschemafile> + <schemalist> + + + + <!-- Global settings --> + + + + <schema> + <key>/schemas/apps/mate-terminal/global/profile_list</key> + <applyto>/apps/mate-terminal/global/profile_list</applyto> + <owner>mate-terminal</owner> + <type>list</type> + <list_type>string</list_type> + <default>[Default]</default> + <locale name="C"> + <short>List of profiles</short> + <long> + List of profiles known to mate-terminal. The list contains + strings naming subdirectories relative to + /apps/mate-terminal/profiles. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/global/default_profile</key> + <applyto>/apps/mate-terminal/global/default_profile</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>Default</default> + <locale name="C"> + <short>Profile to use for new terminals</short> + <long> + Profile to be used when opening a new window or tab. + Must be in profile_list. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/global/use_mnemonics</key> + <applyto>/apps/mate-terminal/global/use_mnemonics</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether the menubar has access keys</short> + <long> + Whether to have Alt+letter access keys for the menubar. + They may interfere with some applications run inside the terminal + so it's possible to turn them off. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/global/use_menu_accelerators</key> + <applyto>/apps/mate-terminal/global/use_menu_accelerators</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether the standard GTK shortcut for menubar access is enabled</short> + <long> + Normally you can access the menubar with F10. This can also + be customized via gtkrc (gtk-menu-bar-accel = + "whatever"). This option allows the standard menubar + accelerator to be disabled. + </long> + </locale> + </schema> + + + <schema> + <key>/schemas/apps/mate-terminal/global/active_encodings</key> + <applyto>/apps/mate-terminal/global/active_encodings</applyto> + <owner>mate-terminal</owner> + <type>list</type> + <list_type>string</list_type> + <locale name="C"> + <default><!-- Translators: Please note that this has to be a list of + valid encodings (which are to be taken from the list in src/encoding.c). + It has to include UTF-8 and the word 'current', which is not to be + translated. This is provided for customization of the default encoding + menu; see bug 144810 for an use case. In most cases, this should be + left alone. -->[UTF-8,current]</default> + <short>List of available encodings</short> + <long> + A subset of possible encodings are presented in + the Encoding submenu. This is a list of encodings + to appear there. The special encoding name "current" + means to display the encoding of the current locale. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/global/confirm_window_close</key> + <applyto>/apps/mate-terminal/global/confirm_window_close</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether to ask for confirmation when closing terminal windows</short> + <long> + Whether to ask for confirmation when closing a terminal window which has + more than one open tab. + </long> + </locale> + </schema> + + + + + <!-- Per-profile settings --> + + + + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/visible_name</key> + <applyto>/apps/mate-terminal/profiles/Default/visible_name</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <locale name="C"> + <default>Default</default> + <short>Human-readable name of the profile</short> + <long> + Human-readable name of the profile. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/default_show_menubar</key> + <applyto>/apps/mate-terminal/profiles/Default/default_show_menubar</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether to show menubar in new windows/tabs</short> + <long> + True if the menubar should be shown in new windows, + for windows/tabs with this profile. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/foreground_color</key> + <applyto>/apps/mate-terminal/profiles/Default/foreground_color</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>#000000</default> + <locale name="C"> + <short>Default color of text in the terminal</short> + <long> + Default color of text in the terminal, as a color + specification (can be HTML-style hex digits, or + a color name such as "red"). + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/background_color</key> + <applyto>/apps/mate-terminal/profiles/Default/background_color</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>#FFFFDD</default> + <locale name="C"> + <short>Default color of terminal background</short> + <long> + Default color of terminal background, as a color + specification (can be HTML-style hex digits, or + a color name such as "red"). + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/bold_color</key> + <applyto>/apps/mate-terminal/profiles/Default/bold_color</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>#000000</default> + <locale name="C"> + <short>Default color of bold text in the terminal</short> + <long> + Default color of bold text in the terminal, as a color + specification (can be HTML-style hex digits, or + a color name such as "red"). + This is ignored if bold_color_same_as_fg is true. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/bold_color_same_as_fg</key> + <applyto>/apps/mate-terminal/profiles/Default/bold_color_same_as_fg</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether bold text should use the same color as normal text</short> + <long> + If true, boldface text will be rendered using the same color as + normal text. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/title_mode</key> + <applyto>/apps/mate-terminal/profiles/Default/title_mode</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>replace</default> + <locale name="C"> + <short>What to do with dynamic title</short> + <long> + If the application in the terminal sets the title + (most typically people have their shell set up to + do this), the dynamically-set title can + erase the configured title, go before it, go after it, + or replace it. The possible values are "replace", + "before", "after", and "ignore". + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/title</key> + <applyto>/apps/mate-terminal/profiles/Default/title</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <locale name="C"> + <default>Terminal</default> + <short>Title for terminal</short> + <long> + Title to display for the terminal window or tab. + This title may be replaced by or combined with + the title set by the application inside the terminal, + depending on the title_mode setting. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/allow_bold</key> + <applyto>/apps/mate-terminal/profiles/Default/allow_bold</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether to allow bold text</short> + <long> + If true, allow applications in the terminal to make + text boldface. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/silent_bell</key> + <applyto>/apps/mate-terminal/profiles/Default/silent_bell</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Whether to silence terminal bell</short> + <long> + If true, don't make a noise when applications send the + escape sequence for the terminal bell. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/word_chars</key> + <applyto>/apps/mate-terminal/profiles/Default/word_chars</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>-A-Za-z0-9,./?%&#:_=+@~</default> + <locale name="C"> + <short>Characters that are considered "part of a word"</short> + <long> + When selecting text by word, sequences of these characters + are considered single words. Ranges can be given as + "A-Z". Literal hyphen (not expressing a range) should be + the first character given. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/use_custom_default_size</key> + <applyto>/apps/mate-terminal/profiles/Default/use_custom_default_size</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Whether to use custom terminal size for new windows</short> + <long> + If true, newly created terminal windows will have custom + size specified by default_size_columns and default_size_rows. + </long> + </locale> + </schema> + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/default_size_columns</key> + <applyto>/apps/mate-terminal/profiles/Default/default_size_columns</applyto> + <owner>mate-terminal</owner> + <type>int</type> + <default>80</default> + <locale name="C"> + <short>Default number of columns</short> + <long> + Number of columns in newly created terminal windows. + Has no effect if use_custom_default_size is not enabled. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/default_size_rows</key> + <applyto>/apps/mate-terminal/profiles/Default/default_size_rows</applyto> + <owner>mate-terminal</owner> + <type>int</type> + <default>24</default> + <locale name="C"> + <short>Default number of rows</short> + <long> + Number of rows in newly created terminal windows. + Has no effect if use_custom_default_size is not enabled. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/scrollbar_position</key> + <applyto>/apps/mate-terminal/profiles/Default/scrollbar_position</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>right</default> + <locale name="C"> + <short>Position of the scrollbar</short> + <long> + Where to put the terminal scrollbar. Possibilities are + "left", "right", and "hidden". + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/scrollback_lines</key> + <applyto>/apps/mate-terminal/profiles/Default/scrollback_lines</applyto> + <owner>mate-terminal</owner> + <type>int</type> + <default>512</default> + <locale name="C"> + <short>Number of lines to keep in scrollback</short> + <long> + Number of scrollback lines to keep around. You can + scroll back in the terminal by this number of lines; + lines that don't fit in the scrollback are discarded. + If scrollback_unlimited is true, this value is ignored. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/scrollback_unlimited</key> + <applyto>/apps/mate-terminal/profiles/Default/scrollback_unlimited</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Whether an unlimited number of lines should be kept in scrollback</short> + <long> + If true, scrollback lines will never be discarded. The scrollback + history is stored on disk temporarily, so this may cause the system + to run out of disk space if there is a lot of output to the + terminal. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/scroll_on_keystroke</key> + <applyto>/apps/mate-terminal/profiles/Default/scroll_on_keystroke</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether to scroll to the bottom when a key is pressed</short> + <long> + If true, pressing a key jumps the scrollbar to the bottom. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/scroll_on_output</key> + <applyto>/apps/mate-terminal/profiles/Default/scroll_on_output</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Whether to scroll to the bottom when there's new output</short> + <long> + If true, whenever there's new output the terminal will scroll + to the bottom. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/exit_action</key> + <applyto>/apps/mate-terminal/profiles/Default/exit_action</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>close</default> + <locale name="C"> + <short>What to do with the terminal when the child command exits</short> + <long> + Possible values are "close" to close the terminal, and + "restart" to restart the command. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/login_shell</key> + <applyto>/apps/mate-terminal/profiles/Default/login_shell</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Whether to launch the command in the terminal as a login shell</short> + <long> + If true, the command inside the terminal will be launched as + a login shell. (argv[0] will have a hyphen in front of it.) + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/update_records</key> + <applyto>/apps/mate-terminal/profiles/Default/update_records</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether to update login records when launching terminal command</short> + <long> + If true, the system login records utmp and wtmp will be updated when the command inside the terminal + is launched. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/use_custom_command</key> + <applyto>/apps/mate-terminal/profiles/Default/use_custom_command</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Whether to run a custom command instead of the shell</short> + <long> + If true, the value of the custom_command setting will + be used in place of running a shell. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/cursor_blink_mode</key> + <applyto>/apps/mate-terminal/profiles/Default/cursor_blink_mode</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>system</default> + <locale name="C"> + <short>Whether to blink the cursor</short> + <long> + The possible values are "system" to use the global cursor blinking + settings, or "on" or "off" to set the mode explicitly. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/cursor_shape</key> + <applyto>/apps/mate-terminal/profiles/Default/cursor_shape</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>block</default> + <locale name="C"> + <short>The cursor appearance</short> + <long> + The possible values are "block" to use a block cursor, "ibeam" to + use a vertical line cursor, or "underline" to use an underline cursor. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/custom_command</key> + <applyto>/apps/mate-terminal/profiles/Default/custom_command</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default></default> + <locale name="C"> + <short>Custom command to use instead of the shell</short> + <long> + Run this command in place of the shell, if + use_custom_command is true. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/icon</key> + <applyto>/apps/mate-terminal/profiles/Default/icon</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default></default> + <locale name="C"> + <short>Icon for terminal window</short> + <long> + Icon to use for tabs/windows containing this profile. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/palette</key> + <applyto>/apps/mate-terminal/profiles/Default/palette</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>#2E2E34343636:#CCCC00000000:#4E4E9A9A0606:#C4C4A0A00000:#34346565A4A4:#757550507B7B:#060698209A9A:#D3D3D7D7CFCF:#555557575353:#EFEF29292929:#8A8AE2E23434:#FCFCE9E94F4F:#72729F9FCFCF:#ADAD7F7FA8A8:#3434E2E2E2E2:#EEEEEEEEECEC</default> + <locale name="C"> + <short>Palette for terminal applications</short> + <long> + Terminals have a 16-color palette that applications inside + the terminal can use. This is that palette, in the form + of a colon-separated list of color names. Color names + should be in hex format e.g. "#FF00FF" + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/font</key> + <applyto>/apps/mate-terminal/profiles/Default/font</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>Monospace 12</default> + <locale name="C"> + <short>Font</short> + <long> + An Pango font name. Examples are "Sans 12" or "Monospace Bold 14". + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/background_type</key> + <applyto>/apps/mate-terminal/profiles/Default/background_type</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>solid</default> + <locale name="C"> + <short>Background type</short> + <long> + Type of terminal background. May be "solid" for a solid color, + "image" for an image, or "transparent" for either real transparency if + a compositing window manager is running, or pseudo-transparency + otherwise. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/background_image</key> + <applyto>/apps/mate-terminal/profiles/Default/background_image</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default></default> + <locale name="C"> + <short>Background image</short> + <long> + Filename of a background image. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/scroll_background</key> + <applyto>/apps/mate-terminal/profiles/Default/scroll_background</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether to scroll background image</short> + <long> + If true, scroll the background image with the foreground + text; if false, keep the image in a fixed position and + scroll the text above it. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/background_darkness</key> + <applyto>/apps/mate-terminal/profiles/Default/background_darkness</applyto> + <owner>mate-terminal</owner> + <type>float</type> + <default>0.5</default> + <locale name="C"> + <short>How much to darken the background image</short> + <long> + A value between 0.0 and 1.0 indicating how much to darken + the background image. 0.0 means no darkness, 1.0 means fully + dark. In the current implementation, there are only two levels of + darkness possible, so the setting behaves as a boolean, + where 0.0 disables the darkening effect. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/backspace_binding</key> + <applyto>/apps/mate-terminal/profiles/Default/backspace_binding</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>ascii-del</default> + <locale name="C"> + <short>Effect of the Backspace key</short> + <long> + Sets what code the backspace key generates. Possible values + are "ascii-del" for the ASCII DEL character, + "control-h" for Control-H (AKA the ASCII BS character), + "escape-sequence" for the escape sequence typically + bound to backspace or delete. "ascii-del" is normally + considered the correct setting for the Backspace key. + </long> + </locale> + </schema> + + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/delete_binding</key> + <applyto>/apps/mate-terminal/profiles/Default/delete_binding</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>escape-sequence</default> + <locale name="C"> + <short>Effect of the Delete key</short> + <long> + Sets what code the delete key generates. Possible values + are "ascii-del" for the ASCII DEL character, + "control-h" for Control-H (AKA the ASCII BS character), + "escape-sequence" for the escape sequence typically + bound to backspace or delete. "escape-sequence" is normally + considered the correct setting for the Delete key. + </long> + </locale> + </schema> + + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/use_theme_colors</key> + <applyto>/apps/mate-terminal/profiles/Default/use_theme_colors</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether to use the colors from the theme for the terminal widget</short> + <long> + If true, the theme color scheme used for text entry boxes will + be used for the terminal, instead of colors provided by the user. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/use_system_font</key> + <applyto>/apps/mate-terminal/profiles/Default/use_system_font</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short>Whether to use the system font</short> + <long> + If true, the terminal will use the desktop-global standard + font if it's monospace (and the most similar font it can + come up with otherwise). + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/profiles/Default/use_skey</key> + <applyto>/apps/mate-terminal/profiles/Default/use_skey</applyto> + <owner>mate-terminal</owner> + <type>bool</type> + <default>true</default> + <locale name="C"> + <short><!-- Translators: S/Key is the name of an application, so it should + not be translated. -->Highlight S/Key challenges</short> + <long><!-- Translators: S/Key is the name of an application, so it should + not be translated. -->Popup a dialog when an S/Key challenge response query is + detected and clicked on. Typing a password into the dialog + will send it to the terminal. + </long> + </locale> + </schema> + + <!-- Keybindings --> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/new_tab</key> + <applyto>/apps/mate-terminal/keybindings/new_tab</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl><Shift>t</default> + <locale name="C"> + <short>Keyboard shortcut to open a new tab</short> + <long> + Keyboard shortcut key for opening a new tab. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/new_window</key> + <applyto>/apps/mate-terminal/keybindings/new_window</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl><Shift>n</default> + <locale name="C"> + <short>Keyboard shortcut to open a new window</short> + <long> + Keyboard shortcut key for opening a new window. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/new_profile</key> + <applyto>/apps/mate-terminal/keybindings/new_profile</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <locale name="C"> + <short>Keyboard shortcut to create a new profile</short> + <long> + Keyboard shortcut key for bringing up the dialog for profile + creation. Expressed as a string in the same format used for + GTK+ resource files. If you set the option to the special + string "disabled", then there will be no keyboard shortcut for this + action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/save_contents</key> + <applyto>/apps/mate-terminal/keybindings/save_contents</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>disabled</default> + <locale name="C"> + <short>Keyboard shortcut to save the current tab contents to file</short> + <long> + Keyboard shortcut key to save the current tab contents to a file. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/close_tab</key> + <applyto>/apps/mate-terminal/keybindings/close_tab</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl><Shift>w</default> + <locale name="C"> + <short>Keyboard shortcut to close a tab</short> + <long> + Keyboard shortcut key for closing a tab. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/close_window</key> + <applyto>/apps/mate-terminal/keybindings/close_window</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl><Shift>q</default> + <locale name="C"> + <short>Keyboard shortcut to close a window</short> + <long> + Keyboard shortcut key for closing a window. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/copy</key> + <applyto>/apps/mate-terminal/keybindings/copy</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl><Shift>c</default> + <locale name="C"> + <short>Keyboard shortcut to copy text</short> + <long> + Keyboard shortcut key for copying selected text to the + clipboard. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/paste</key> + <applyto>/apps/mate-terminal/keybindings/paste</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl><Shift>v</default> + <locale name="C"> + <short>Keyboard shortcut to paste text</short> + <long> + Keyboard shortcut key for pasting the contents of the + clipboard into the terminal. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/full_screen</key> + <applyto>/apps/mate-terminal/keybindings/full_screen</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>F11</default> + <locale name="C"> + <short>Keyboard shortcut to toggle full screen mode</short> + <long> + Keyboard shortcut key for toggling full screen mode. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + <schema> + <key>/schemas/apps/mate-terminal/keybindings/toggle_menubar</key> + <applyto>/apps/mate-terminal/keybindings/toggle_menubar</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <locale name="C"> + <short>Keyboard shortcut to toggle the visibility of the menubar</short> + <long> + Keyboard shortcut key to toggle the visibility of the menubar. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/set_terminal_title</key> + <applyto>/apps/mate-terminal/keybindings/set_terminal_title</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <locale name="C"> + <short>Keyboard shortcut to set the terminal title</short> + <long> + Keyboard shortcut key to set the terminal title. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/reset</key> + <applyto>/apps/mate-terminal/keybindings/reset</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <locale name="C"> + <short>Keyboard shortcut to reset the terminal</short> + <long> + Keyboard shortcut key to reset the terminal. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/reset_and_clear</key> + <applyto>/apps/mate-terminal/keybindings/reset_and_clear</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <locale name="C"> + <short>Keyboard shortcut to reset and clear the terminal</short> + <long> + Keyboard shortcut key to reset and clear the terminal. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/prev_tab</key> + <applyto>/apps/mate-terminal/keybindings/prev_tab</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Control>Page_Up</default> + <locale name="C"> + <short>Keyboard shortcut to switch to the previous tab</short> + <long> + Keyboard shortcut key to switch to the previous tab. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/next_tab</key> + <applyto>/apps/mate-terminal/keybindings/next_tab</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Control>Page_Down</default> + <locale name="C"> + <short>Keyboard shortcut to switch to the next tab</short> + <long> + Keyboard shortcut key to switch to the next tab. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/move_tab_left</key> + <applyto>/apps/mate-terminal/keybindings/move_tab_left</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl><Shift>Page_Up</default> + <locale name="C"> + <short>Accelerator to move the current tab to the left.</short> + <long> + Accelerator key to move the current tab to the left. Expressed as a + string in the same format used for GTK+ resource files. If you set + the option to the special string "disabled", then there will be no + keybinding for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/move_tab_right</key> + <applyto>/apps/mate-terminal/keybindings/move_tab_right</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl><Shift>Page_Down</default> + <locale name="C"> + <short>Accelerator to move the current tab to the right.</short> + <long> + Accelerator key to move the current tab to the right. Expressed as a + string in the same format used for GTK+ resource files. If you set + the option to the special string "disabled", then there will be no + keybinding for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/detach_tab</key> + <applyto>/apps/mate-terminal/keybindings/detach_tab</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <locale name="C"> + <short>Accelerator to detach current tab.</short> + <long> + Accelerator key to detach current tab. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keybinding for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_1</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_1</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Alt>1</default> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 1</short> + <long> + Keyboard shortcut key for switch to tab 1. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_2</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_2</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Alt>2</default> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 2</short> + <long> + Keyboard shortcut key for switch to tab 2. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_3</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_3</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Alt>3</default> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 3</short> + <long> + Keyboard shortcut key for switch to tab 3. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_4</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_4</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Alt>4</default> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 4</short> + <long> + Keyboard shortcut key for switch to tab 4. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_5</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_5</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Alt>5</default> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 5</short> + <long> + Keyboard shortcut key for switch to tab 5. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_6</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_6</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Alt>6</default> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 6</short> + <long> + Keyboard shortcut key for switch to tab 6. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_7</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_7</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Alt>7</default> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 7</short> + <long> + Keyboard shortcut key for switch to tab 7. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_8</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_8</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Alt>8</default> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 8</short> + <long> + Keyboard shortcut key for switch to tab 8. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_9</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_9</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Alt>9</default> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 9</short> + <long> + Keyboard shortcut key for switch to tab 9. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_10</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_10</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Alt>0</default> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 10</short> + <long> + Keyboard shortcut key for switch to tab 10. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_11</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_11</applyto> + <owner>mate-terminal</owner> + <type>string</type> +<!-- no default --> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 11</short> + <long> + Keyboard shortcut key for switch to tab 11. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/switch_to_tab_12</key> + <applyto>/apps/mate-terminal/keybindings/switch_to_tab_12</applyto> + <owner>mate-terminal</owner> + <type>string</type> +<!-- no default --> + <locale name="C"> + <short>Keyboard shortcut to switch to tab 12</short> + <long> + Keyboard shortcut key for switch to tab 12. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/help</key> + <applyto>/apps/mate-terminal/keybindings/help</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default>F1</default> + <locale name="C"> + <short>Keyboard shortcut to launch help</short> + <long> + Keyboard shortcut key for launching help. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/zoom_in</key> + <applyto>/apps/mate-terminal/keybindings/zoom_in</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl>plus</default> + <locale name="C"> + <short>Keyboard shortcut to make font larger</short> + <long> + Keyboard shortcut key for making font larger. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/zoom_out</key> + <applyto>/apps/mate-terminal/keybindings/zoom_out</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl>minus</default> + <locale name="C"> + <short>Keyboard shortcut to make font smaller</short> + <long> + Keyboard shortcut key for making font smaller. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/mate-terminal/keybindings/zoom_normal</key> + <applyto>/apps/mate-terminal/keybindings/zoom_normal</applyto> + <owner>mate-terminal</owner> + <type>string</type> + <default><Ctrl>0</default> + <locale name="C"> + <short>Keyboard shortcut to make font normal-size</short> + <long> + Keyboard shortcut key for making font the normal size. Expressed as a string + in the same format used for GTK+ resource files. + If you set the option to the special string "disabled", then there + will be no keyboard shortcut for this action. + </long> + </locale> + </schema> + + </schemalist> +</mateconfschemafile> diff --git a/src/profile-editor.c b/src/profile-editor.c new file mode 100644 index 0000000..6ac0b34 --- /dev/null +++ b/src/profile-editor.c @@ -0,0 +1,921 @@ +/* + * Copyright © 2002 Havoc Pennington + * Copyright © 2002 Mathias Hasselmann + * Copyright © 2008 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <math.h> + +#include <glib.h> +#include <gio/gio.h> + +#include "terminal-intl.h" +#include "profile-editor.h" +#include "terminal-util.h" + +typedef struct _TerminalColorScheme TerminalColorScheme; + +struct _TerminalColorScheme +{ + const char *name; + const GdkColor foreground; + const GdkColor background; +}; + +static const TerminalColorScheme color_schemes[] = { + { N_("Black on light yellow"), + { 0, 0x0000, 0x0000, 0x0000 }, { 0, 0xFFFF, 0xFFFF, 0xDDDD } }, + { N_("Black on white"), + { 0, 0x0000, 0x0000, 0x0000 }, { 0, 0xFFFF, 0xFFFF, 0xFFFF } }, + { N_("Gray on black"), + { 0, 0xAAAA, 0xAAAA, 0xAAAA }, { 0, 0x0000, 0x0000, 0x0000 } }, + { N_("Green on black"), + { 0, 0x0000, 0xFFFF, 0x0000 }, { 0, 0x0000, 0x0000, 0x0000 } }, + { N_("White on black"), + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, { 0, 0x0000, 0x0000, 0x0000 } } +}; + +static void profile_forgotten_cb (TerminalProfile *profile, + GtkWidget *editor); + +static void profile_notify_sensitivity_cb (TerminalProfile *profile, + GParamSpec *pspec, + GtkWidget *editor); + +static void profile_colors_notify_scheme_combo_cb (TerminalProfile *profile, + GParamSpec *pspec, + GtkComboBox *combo); + +static void profile_palette_notify_scheme_combo_cb (TerminalProfile *profile, + GParamSpec *pspec, + GtkComboBox *combo); + +static void profile_palette_notify_colorpickers_cb (TerminalProfile *profile, + GParamSpec *pspec, + GtkWidget *editor); + +static GtkWidget* +profile_editor_get_widget (GtkWidget *editor, + const char *widget_name) +{ + GtkBuilder *builder; + + builder = g_object_get_data (G_OBJECT (editor), "builder"); + g_assert (builder != NULL); + + return (GtkWidget *) gtk_builder_get_object (builder, widget_name); +} + +static void +widget_and_labels_set_sensitive (GtkWidget *widget, gboolean sensitive) +{ + GList *labels, *i; + + labels = gtk_widget_list_mnemonic_labels (widget); + for (i = labels; i; i = i->next) + { + gtk_widget_set_sensitive (GTK_WIDGET (i->data), sensitive); + } + g_list_free (labels); + + gtk_widget_set_sensitive (widget, sensitive); +} + +static void +profile_forgotten_cb (TerminalProfile *profile, + GtkWidget *editor) +{ + gtk_widget_destroy (editor); +} + +static void +profile_notify_sensitivity_cb (TerminalProfile *profile, + GParamSpec *pspec, + GtkWidget *editor) +{ + TerminalBackgroundType bg_type; + const char *prop_name; + + if (pspec) + prop_name = pspec->name; + else + prop_name = NULL; + +#define SET_SENSITIVE(name, setting) widget_and_labels_set_sensitive (profile_editor_get_widget (editor, name), setting) + + if (!prop_name || + prop_name == I_(TERMINAL_PROFILE_USE_CUSTOM_COMMAND) || + prop_name == I_(TERMINAL_PROFILE_CUSTOM_COMMAND)) + { + gboolean use_custom_command_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_USE_CUSTOM_COMMAND); + SET_SENSITIVE ("use-custom-command-checkbutton", !use_custom_command_locked); + SET_SENSITIVE ("custom-command-box", + terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_CUSTOM_COMMAND) && + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_CUSTOM_COMMAND)); + } + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_BACKGROUND_TYPE)) + { + gboolean bg_type_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_BACKGROUND_TYPE); + SET_SENSITIVE ("solid-radiobutton", !bg_type_locked); + SET_SENSITIVE ("image-radiobutton", !bg_type_locked); + SET_SENSITIVE ("transparent-radiobutton", !bg_type_locked); + + bg_type = terminal_profile_get_property_enum (profile, TERMINAL_PROFILE_BACKGROUND_TYPE); + if (bg_type == TERMINAL_BACKGROUND_IMAGE) + { + SET_SENSITIVE ("background-image-filechooser", !terminal_profile_property_locked (profile, TERMINAL_PROFILE_BACKGROUND_IMAGE_FILE)); + SET_SENSITIVE ("scroll-background-checkbutton", !terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLL_BACKGROUND)); + SET_SENSITIVE ("darken-background-vbox", !terminal_profile_property_locked (profile, TERMINAL_PROFILE_BACKGROUND_DARKNESS)); + } + else if (bg_type == TERMINAL_BACKGROUND_TRANSPARENT) + { + SET_SENSITIVE ("background-image-filechooser", FALSE); + SET_SENSITIVE ("scroll-background-checkbutton", FALSE); + SET_SENSITIVE ("darken-background-vbox", !terminal_profile_property_locked (profile, TERMINAL_PROFILE_BACKGROUND_DARKNESS)); + } + else + { + SET_SENSITIVE ("background-image-filechooser", FALSE); + SET_SENSITIVE ("scroll-background-checkbutton", FALSE); + SET_SENSITIVE ("darken-background-vbox", FALSE); + } + } + + if (!prop_name || + prop_name == I_(TERMINAL_PROFILE_USE_SYSTEM_FONT) || + prop_name == I_(TERMINAL_PROFILE_FONT)) + { + SET_SENSITIVE ("font-hbox", + !terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_SYSTEM_FONT) && + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_FONT)); + SET_SENSITIVE ("system-font-checkbutton", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_USE_SYSTEM_FONT)); + } + + if (!prop_name || + prop_name == I_(TERMINAL_PROFILE_FOREGROUND_COLOR) || + prop_name == I_(TERMINAL_PROFILE_BACKGROUND_COLOR) || + prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR) || + prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG) || + prop_name == I_(TERMINAL_PROFILE_USE_THEME_COLORS)) + { + gboolean bg_locked, use_theme_colors, fg_locked; + + use_theme_colors = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_THEME_COLORS); + fg_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_FOREGROUND_COLOR); + bg_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_BACKGROUND_COLOR); + + SET_SENSITIVE ("foreground-colorpicker", !use_theme_colors && !fg_locked); + SET_SENSITIVE ("foreground-colorpicker-label", !use_theme_colors && !fg_locked); + SET_SENSITIVE ("background-colorpicker", !use_theme_colors && !bg_locked); + SET_SENSITIVE ("background-colorpicker-label", !use_theme_colors && !bg_locked); + SET_SENSITIVE ("color-scheme-combobox", !use_theme_colors && !fg_locked && !bg_locked); + SET_SENSITIVE ("color-scheme-combobox-label", !use_theme_colors && !fg_locked && !bg_locked); + } + + if (!prop_name || + prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR) || + prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG) || + prop_name == I_(TERMINAL_PROFILE_USE_THEME_COLORS)) + { + gboolean bold_locked, bold_same_as_fg_locked, bold_same_as_fg, use_theme_colors; + + use_theme_colors = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_THEME_COLORS); + bold_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_BOLD_COLOR); + bold_same_as_fg_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG); + bold_same_as_fg = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG); + + SET_SENSITIVE ("bold-color-same-as-fg-checkbox", !use_theme_colors && !bold_same_as_fg_locked); + SET_SENSITIVE ("bold-colorpicker", !use_theme_colors && !bold_locked && !bold_same_as_fg); + SET_SENSITIVE ("bold-colorpicker-label", !use_theme_colors && ((!bold_same_as_fg && !bold_locked) || !bold_same_as_fg_locked)); + } + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_VISIBLE_NAME)) + SET_SENSITIVE ("profile-name-entry", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_VISIBLE_NAME)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_DEFAULT_SHOW_MENUBAR)) + SET_SENSITIVE ("show-menubar-checkbutton", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_DEFAULT_SHOW_MENUBAR)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_TITLE)) + SET_SENSITIVE ("title-entry", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_TITLE)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_TITLE_MODE)) + SET_SENSITIVE ("title-mode-combobox", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_TITLE_MODE)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_ALLOW_BOLD)) + SET_SENSITIVE ("allow-bold-checkbutton", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_ALLOW_BOLD)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SILENT_BELL)) + SET_SENSITIVE ("bell-checkbutton", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_SILENT_BELL)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_WORD_CHARS)) + SET_SENSITIVE ("word-chars-entry", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_WORD_CHARS)); + + if (!prop_name || + prop_name == I_(TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE) || + prop_name == I_(TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS) || + prop_name == I_(TERMINAL_PROFILE_DEFAULT_SIZE_ROWS)) + { + gboolean use_custom_default_size_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE); + gboolean use_custom_default_size = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE); + gboolean columns_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS); + gboolean rows_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_DEFAULT_SIZE_ROWS); + + SET_SENSITIVE ("use-custom-default-size-checkbutton", !use_custom_default_size_locked); + SET_SENSITIVE ("default-size-hbox", use_custom_default_size); + SET_SENSITIVE ("default-size-label", (!columns_locked || !rows_locked)); + SET_SENSITIVE ("default-size-columns-label", !columns_locked); + SET_SENSITIVE ("default-size-columns-spinbutton", !columns_locked); + SET_SENSITIVE ("default-size-rows-label", !rows_locked); + SET_SENSITIVE ("default-size-rows-spinbutton", !rows_locked); + } + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLLBAR_POSITION)) + SET_SENSITIVE ("scrollbar-position-combobox", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLLBAR_POSITION)); + + if (!prop_name || + prop_name == I_(TERMINAL_PROFILE_SCROLLBACK_LINES) || + prop_name == I_(TERMINAL_PROFILE_SCROLLBACK_UNLIMITED)) + { + gboolean scrollback_lines_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLLBACK_LINES); + gboolean scrollback_unlimited_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLLBACK_UNLIMITED); + gboolean scrollback_unlimited = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_SCROLLBACK_UNLIMITED); + + SET_SENSITIVE ("scrollback-label", !scrollback_lines_locked); + SET_SENSITIVE ("scrollback-box", !scrollback_lines_locked && !scrollback_unlimited); + SET_SENSITIVE ("scrollback-unlimited-checkbutton", !scrollback_lines_locked && !scrollback_unlimited_locked); + } + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE)) + SET_SENSITIVE ("scroll-on-keystroke-checkbutton", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLL_ON_OUTPUT)) + SET_SENSITIVE ("scroll-on-output-checkbutton", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLL_ON_OUTPUT)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_EXIT_ACTION)) + SET_SENSITIVE ("exit-action-combobox", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_EXIT_ACTION)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_LOGIN_SHELL)) + SET_SENSITIVE ("login-shell-checkbutton", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_LOGIN_SHELL)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_UPDATE_RECORDS)) + SET_SENSITIVE ("update-records-checkbutton", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_UPDATE_RECORDS)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_PALETTE)) + { + gboolean palette_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_PALETTE); + SET_SENSITIVE ("palette-combobox", !palette_locked); + SET_SENSITIVE ("palette-table", !palette_locked); + } + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_BACKSPACE_BINDING)) + SET_SENSITIVE ("backspace-binding-combobox", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_BACKSPACE_BINDING)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_DELETE_BINDING)) + SET_SENSITIVE ("delete-binding-combobox", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_DELETE_BINDING)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_USE_THEME_COLORS)) + SET_SENSITIVE ("use-theme-colors-checkbutton", + !terminal_profile_property_locked (profile, TERMINAL_PROFILE_USE_THEME_COLORS)); + +#undef SET_INSENSITIVE +} + +static void +color_scheme_combo_changed_cb (GtkWidget *combo, + GParamSpec *pspec, + TerminalProfile *profile) +{ + guint i; + + i = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); + + if (i < G_N_ELEMENTS (color_schemes)) + { + g_signal_handlers_block_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo); + g_object_set (profile, + TERMINAL_PROFILE_FOREGROUND_COLOR, &color_schemes[i].foreground, + TERMINAL_PROFILE_BACKGROUND_COLOR, &color_schemes[i].background, + NULL); + g_signal_handlers_unblock_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo); + } + else + { + /* "custom" selected, no change */ + } +} + +static void +profile_colors_notify_scheme_combo_cb (TerminalProfile *profile, + GParamSpec *pspec, + GtkComboBox *combo) +{ + const GdkColor *fg, *bg; + guint i; + + fg = terminal_profile_get_property_boxed (profile, TERMINAL_PROFILE_FOREGROUND_COLOR); + bg = terminal_profile_get_property_boxed (profile, TERMINAL_PROFILE_BACKGROUND_COLOR); + + if (fg && bg) + { + for (i = 0; i < G_N_ELEMENTS (color_schemes); ++i) + { + if (gdk_color_equal (fg, &color_schemes[i].foreground) && + gdk_color_equal (bg, &color_schemes[i].background)) + break; + } + } + else + { + i = G_N_ELEMENTS (color_schemes); + } + /* If we didn't find a match, then we get the last combo box item which is "custom" */ + + g_signal_handlers_block_by_func (combo, G_CALLBACK (color_scheme_combo_changed_cb), profile); + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), i); + g_signal_handlers_unblock_by_func (combo, G_CALLBACK (color_scheme_combo_changed_cb), profile); +} + +static void +palette_scheme_combo_changed_cb (GtkComboBox *combo, + GParamSpec *pspec, + TerminalProfile *profile) +{ + int i; + + i = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); + + g_signal_handlers_block_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo); + if (i < TERMINAL_PALETTE_N_BUILTINS) + terminal_profile_set_palette_builtin (profile, i); + else + { + /* "custom" selected, no change */ + } + g_signal_handlers_unblock_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo); +} + +static void +profile_palette_notify_scheme_combo_cb (TerminalProfile *profile, + GParamSpec *pspec, + GtkComboBox *combo) +{ + guint i; + + if (!terminal_profile_get_palette_is_builtin (profile, &i)) + /* If we didn't find a match, then we want the last combo + * box item which is "custom" + */ + i = TERMINAL_PALETTE_N_BUILTINS; + + g_signal_handlers_block_by_func (combo, G_CALLBACK (palette_scheme_combo_changed_cb), profile); + gtk_combo_box_set_active (combo, i); + g_signal_handlers_unblock_by_func (combo, G_CALLBACK (palette_scheme_combo_changed_cb), profile); +} + +static void +palette_color_notify_cb (GtkColorButton *button, + GParamSpec *pspec, + TerminalProfile *profile) +{ + GtkWidget *editor; + GdkColor color; + guint i; + + gtk_color_button_get_color (button, &color); + i = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (button), "palette-entry-index")); + + editor = gtk_widget_get_toplevel (GTK_WIDGET (button)); + g_signal_handlers_block_by_func (profile, G_CALLBACK (profile_palette_notify_colorpickers_cb), editor); + terminal_profile_modify_palette_entry (profile, i, &color); + g_signal_handlers_unblock_by_func (profile, G_CALLBACK (profile_palette_notify_colorpickers_cb), editor); +} + +static void +profile_palette_notify_colorpickers_cb (TerminalProfile *profile, + GParamSpec *pspec, + GtkWidget *editor) +{ + GtkWidget *w; + GdkColor colors[TERMINAL_PALETTE_SIZE]; + guint n_colors, i; + + n_colors = G_N_ELEMENTS (colors); + terminal_profile_get_palette (profile, colors, &n_colors); + + n_colors = MIN (n_colors, TERMINAL_PALETTE_SIZE); + for (i = 0; i < n_colors; i++) + { + char name[32]; + GdkColor old_color; + + g_snprintf (name, sizeof (name), "palette-colorpicker-%d", i + 1); + w = profile_editor_get_widget (editor, name); + + gtk_color_button_get_color (GTK_COLOR_BUTTON (w), &old_color); + if (!gdk_color_equal (&old_color, &colors[i])) + { + g_signal_handlers_block_by_func (w, G_CALLBACK (palette_color_notify_cb), profile); + gtk_color_button_set_color (GTK_COLOR_BUTTON (w), &colors[i]); + g_signal_handlers_unblock_by_func (w, G_CALLBACK (palette_color_notify_cb), profile); + } + } +} + +static void +custom_command_entry_changed_cb (GtkEntry *entry) +{ +#if GTK_CHECK_VERSION (2, 16, 0) + const char *command; + GError *error = NULL; + + command = gtk_entry_get_text (entry); + + if (g_shell_parse_argv (command, NULL, NULL, &error)) + { + gtk_entry_set_icon_from_stock (entry, GTK_PACK_END, NULL); + } + else + { + char *tooltip; + + gtk_entry_set_icon_from_stock (entry, GTK_PACK_END, GTK_STOCK_DIALOG_WARNING); + + tooltip = g_strdup_printf (_("Error parsing command: %s"), error->message); + gtk_entry_set_icon_tooltip_text (entry, GTK_PACK_END, tooltip); + g_free (tooltip); + + g_error_free (error); + } +#endif /* GTK+ >= 2.16.0 */ +} + +static void +visible_name_entry_changed_cb (GtkEntry *entry, + GtkWindow *window) +{ + const char *visible_name; + char *text; + + visible_name = gtk_entry_get_text (entry); + + text = g_strdup_printf (_("Editing Profile “%s”"), visible_name); + gtk_window_set_title (window, text); + g_free (text); +} + +static void +reset_compat_defaults_cb (GtkWidget *button, + TerminalProfile *profile) +{ + terminal_profile_reset_property (profile, TERMINAL_PROFILE_DELETE_BINDING); + terminal_profile_reset_property (profile, TERMINAL_PROFILE_BACKSPACE_BINDING); +} + +/* + * initialize widgets + */ + +static void +init_color_scheme_menu (GtkWidget *combo_box) +{ + int i; + + i = G_N_ELEMENTS (color_schemes); + while (i > 0) + { + gtk_combo_box_prepend_text (GTK_COMBO_BOX (combo_box), + _(color_schemes[--i].name)); + } +} + +static char* +format_percent_value (GtkScale *scale, + double val, + void *data) +{ + return g_strdup_printf ("%d%%", (int) (val * 100.0 + 0.5)); +} + +static void +init_background_darkness_scale (GtkWidget *scale) +{ + g_signal_connect (scale, "format-value", + G_CALLBACK (format_percent_value), + NULL); +} + + +static void +editor_response_cb (GtkWidget *editor, + int response, + gpointer use_data) +{ + if (response == GTK_RESPONSE_HELP) + { + terminal_util_show_help ("mate-terminal-prefs", GTK_WINDOW (editor)); + return; + } + + gtk_widget_destroy (editor); +} + +#if 0 +static GdkPixbuf * +create_preview_pixbuf (const gchar *filename) +{ + GdkPixbuf *pixbuf = NULL; + MateThumbnailFactory *thumbs; + const char *mime_type = NULL; + GFile *gfile; + GFileInfo *file_info; + + if (filename == NULL) + return NULL; + + gfile = g_file_new_for_uri (filename); + file_info = g_file_query_info (gfile, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, NULL); + if (file_info != NULL) + mime_type = g_file_info_get_content_type (file_info); + + g_object_unref (gfile); + + if (mime_type != NULL) + { + thumbs = mate_thumbnail_factory_new (MATE_THUMBNAIL_SIZE_NORMAL); + + pixbuf = mate_thumbnail_factory_generate_thumbnail (thumbs, + filename, + mime_type); + g_object_unref (thumbs); + } + + if (file_info != NULL) + g_object_unref (file_info); + + return pixbuf; +} + +static void +update_image_preview (GtkFileChooser *chooser) +{ + GtkWidget *image; + gchar *file; + + image = gtk_file_chooser_get_preview_widget (GTK_FILE_CHOOSER (chooser)); + file = gtk_file_chooser_get_preview_uri (chooser); + + if (file != NULL) { + + GdkPixbuf *pixbuf = NULL; + + pixbuf = create_preview_pixbuf (file); + g_free (file); + + if (pixbuf != NULL) { + gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); + g_object_unref (pixbuf); + } + else { + gtk_image_set_from_stock (GTK_IMAGE (image), + "gtk-dialog-question", + GTK_ICON_SIZE_DIALOG); + } + } + gtk_file_chooser_set_preview_widget_active (chooser, file != NULL); +} +#endif + +static void +setup_background_filechooser (GtkWidget *filechooser, + TerminalProfile *profile) +{ + GtkFileFilter *filter; + const char *home_dir; + + filter = gtk_file_filter_new (); + gtk_file_filter_add_pixbuf_formats (filter); + gtk_file_filter_set_name (filter, _("Images")); + gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (filechooser), filter); + + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filechooser), TRUE); + + /* Start filechooser in $HOME instead of the current dir of the factory which is "/" */ + home_dir = g_get_home_dir (); + if (home_dir) + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filechooser), home_dir); + +#if 0 + GtkWidget *image_preview; + GdkPixbuf *pixbuf = NULL; + + image_preview = gtk_image_new (); + /* FIXMchpe this is bogus */ + pixbuf = create_preview_pixbuf (terminal_profile_get_property_string (profile, TERMINAL_PROFILE_BACKGROUND_IMAGE_FILE)); + if (pixbuf != NULL) + { + gtk_image_set_from_pixbuf (GTK_IMAGE (image_preview), pixbuf); + g_object_unref (pixbuf); + } + else + { + gtk_image_set_from_stock (GTK_IMAGE (image_preview), + "gtk-dialog-question", + GTK_ICON_SIZE_DIALOG); + } + + gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (filechooser), + image_preview); + gtk_file_chooser_set_use_preview_label (GTK_FILE_CHOOSER (filechooser), + FALSE); + gtk_widget_set_size_request (image_preview, 128, -1); + gtk_widget_show (image_preview); + + g_signal_connect (filechooser, "update-preview", + G_CALLBACK (update_image_preview), NULL); +#endif +} + +static void +profile_editor_destroyed (GtkWidget *editor, + TerminalProfile *profile) +{ + g_signal_handlers_disconnect_by_func (profile, G_CALLBACK (profile_forgotten_cb), editor); + g_signal_handlers_disconnect_by_func (profile, G_CALLBACK (profile_notify_sensitivity_cb), editor); + g_signal_handlers_disconnect_matched (profile, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + G_CALLBACK (profile_colors_notify_scheme_combo_cb), NULL); + g_signal_handlers_disconnect_matched (profile, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + G_CALLBACK (profile_palette_notify_scheme_combo_cb), NULL); + g_signal_handlers_disconnect_matched (profile, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + G_CALLBACK (profile_palette_notify_colorpickers_cb), NULL); + + g_object_set_data (G_OBJECT (profile), "editor-window", NULL); + g_object_set_data (G_OBJECT (editor), "builder", NULL); +} + +static void +terminal_profile_editor_focus_widget (GtkWidget *editor, + const char *widget_name) +{ + GtkBuilder *builder; + GtkWidget *widget, *page, *page_parent; + + if (widget_name == NULL) + return; + + builder = g_object_get_data (G_OBJECT (editor), "builder"); + widget = GTK_WIDGET (gtk_builder_get_object (builder, widget_name)); + if (widget == NULL) + return; + + page = widget; + while (page != NULL && + (page_parent = gtk_widget_get_parent (page)) != NULL && + !GTK_IS_NOTEBOOK (page_parent)) + page = page_parent; + + page_parent = gtk_widget_get_parent (page); + if (page != NULL && GTK_IS_NOTEBOOK (page_parent)) { + GtkNotebook *notebook; + + notebook = GTK_NOTEBOOK (page_parent); + gtk_notebook_set_current_page (notebook, gtk_notebook_page_num (notebook, page)); + } + + if (gtk_widget_is_sensitive (widget)) + gtk_widget_grab_focus (widget); +} + +/** + * terminal_profile_edit: + * @profile: a #TerminalProfile + * @transient_parent: a #GtkWindow, or %NULL + * @widget_name: a widget name in the profile editor's UI, or %NULL + * + * Shows the profile editor with @profile, anchored to @transient_parent. + * If @widget_name is non-%NULL, focuses the corresponding widget and + * switches the notebook to its containing page. + */ +void +terminal_profile_edit (TerminalProfile *profile, + GtkWindow *transient_parent, + const char *widget_name) +{ + char *path; + GtkBuilder *builder; + GError *error = NULL; + GtkWidget *editor, *w; + guint i; + + editor = g_object_get_data (G_OBJECT (profile), "editor-window"); + if (editor) + { + terminal_profile_editor_focus_widget (editor, widget_name); + + gtk_window_set_transient_for (GTK_WINDOW (editor), + GTK_WINDOW (transient_parent)); + gtk_window_present (GTK_WINDOW (editor)); + return; + } + + path = g_build_filename (TERM_PKGDATADIR, "profile-preferences.ui", NULL); + builder = gtk_builder_new (); + if (!gtk_builder_add_from_file (builder, path, &error)) { + g_warning ("Failed to load %s: %s\n", path, error->message); + g_error_free (error); + g_free (path); + g_object_unref (builder); + return; + } + g_free (path); + + editor = (GtkWidget *) gtk_builder_get_object (builder, "profile-editor-dialog"); + g_object_set_data_full (G_OBJECT (editor), "builder", + builder, (GDestroyNotify) g_object_unref); + + /* Store the dialogue on the profile, so we can acccess it above to check if + * there's already a profile editor for this profile. + */ + g_object_set_data (G_OBJECT (profile), "editor-window", editor); + + g_signal_connect (editor, "destroy", + G_CALLBACK (profile_editor_destroyed), + profile); + + g_signal_connect (editor, "response", + G_CALLBACK (editor_response_cb), + NULL); + + w = (GtkWidget *) gtk_builder_get_object (builder, "color-scheme-combobox"); + init_color_scheme_menu (w); + + w = (GtkWidget *) gtk_builder_get_object (builder, "darken-background-scale"); + init_background_darkness_scale (w); + + w = (GtkWidget *) gtk_builder_get_object (builder, "background-image-filechooser"); + setup_background_filechooser (w, profile); + + /* Hook up the palette colorpickers and combo box */ + + for (i = 0; i < TERMINAL_PALETTE_SIZE; ++i) + { + char name[32]; + char *text; + + g_snprintf (name, sizeof (name), "palette-colorpicker-%u", i + 1); + w = (GtkWidget *) gtk_builder_get_object (builder, name); + + g_object_set_data (G_OBJECT (w), "palette-entry-index", GUINT_TO_POINTER (i)); + + text = g_strdup_printf (_("Choose Palette Color %d"), i + 1); + gtk_color_button_set_title (GTK_COLOR_BUTTON (w), text); + g_free (text); + + text = g_strdup_printf (_("Palette entry %d"), i + 1); + gtk_widget_set_tooltip_text (w, text); + g_free (text); + + g_signal_connect (w, "notify::color", + G_CALLBACK (palette_color_notify_cb), + profile); + } + + profile_palette_notify_colorpickers_cb (profile, NULL, editor); + g_signal_connect (profile, "notify::" TERMINAL_PROFILE_PALETTE, + G_CALLBACK (profile_palette_notify_colorpickers_cb), + editor); + + w = (GtkWidget *) gtk_builder_get_object (builder, "palette-combobox"); + g_signal_connect (w, "notify::active", + G_CALLBACK (palette_scheme_combo_changed_cb), + profile); + + profile_palette_notify_scheme_combo_cb (profile, NULL, GTK_COMBO_BOX (w)); + g_signal_connect (profile, "notify::" TERMINAL_PROFILE_PALETTE, + G_CALLBACK (profile_palette_notify_scheme_combo_cb), + w); + + /* Hook up the color scheme pickers and combo box */ + w = (GtkWidget *) gtk_builder_get_object (builder, "color-scheme-combobox"); + g_signal_connect (w, "notify::active", + G_CALLBACK (color_scheme_combo_changed_cb), + profile); + + profile_colors_notify_scheme_combo_cb (profile, NULL, GTK_COMBO_BOX (w)); + g_signal_connect (profile, "notify::" TERMINAL_PROFILE_FOREGROUND_COLOR, + G_CALLBACK (profile_colors_notify_scheme_combo_cb), + w); + g_signal_connect (profile, "notify::" TERMINAL_PROFILE_BACKGROUND_COLOR, + G_CALLBACK (profile_colors_notify_scheme_combo_cb), + w); + +#define CONNECT_WITH_FLAGS(name, prop, flags) terminal_util_bind_object_property_to_widget (G_OBJECT (profile), prop, (GtkWidget *) gtk_builder_get_object (builder, name), flags) +#define CONNECT(name, prop) CONNECT_WITH_FLAGS (name, prop, 0) +#define SET_ENUM_VALUE(name, value) g_object_set_data (gtk_builder_get_object (builder, name), "enum-value", GINT_TO_POINTER (value)) + + w = GTK_WIDGET (gtk_builder_get_object (builder, "custom-command-entry")); + custom_command_entry_changed_cb (GTK_ENTRY (w)); + g_signal_connect (w, "changed", + G_CALLBACK (custom_command_entry_changed_cb), NULL); + w = GTK_WIDGET (gtk_builder_get_object (builder, "profile-name-entry")); + g_signal_connect (w, "changed", + G_CALLBACK (visible_name_entry_changed_cb), editor); + + g_signal_connect (gtk_builder_get_object (builder, "reset-compat-defaults-button"), + "clicked", + G_CALLBACK (reset_compat_defaults_cb), + profile); + + SET_ENUM_VALUE ("image-radiobutton", TERMINAL_BACKGROUND_IMAGE); + SET_ENUM_VALUE ("solid-radiobutton", TERMINAL_BACKGROUND_SOLID); + SET_ENUM_VALUE ("transparent-radiobutton", TERMINAL_BACKGROUND_TRANSPARENT); + CONNECT ("allow-bold-checkbutton", TERMINAL_PROFILE_ALLOW_BOLD); + CONNECT ("background-colorpicker", TERMINAL_PROFILE_BACKGROUND_COLOR); + CONNECT ("background-image-filechooser", TERMINAL_PROFILE_BACKGROUND_IMAGE_FILE); + CONNECT ("backspace-binding-combobox", TERMINAL_PROFILE_BACKSPACE_BINDING); + CONNECT ("bold-color-same-as-fg-checkbox", TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG); + CONNECT ("bold-colorpicker", TERMINAL_PROFILE_BOLD_COLOR); + CONNECT ("cursor-shape-combobox", TERMINAL_PROFILE_CURSOR_SHAPE); + CONNECT ("custom-command-entry", TERMINAL_PROFILE_CUSTOM_COMMAND); + CONNECT ("darken-background-scale", TERMINAL_PROFILE_BACKGROUND_DARKNESS); + CONNECT ("default-size-columns-spinbutton", TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS); + CONNECT ("default-size-rows-spinbutton", TERMINAL_PROFILE_DEFAULT_SIZE_ROWS); + CONNECT ("delete-binding-combobox", TERMINAL_PROFILE_DELETE_BINDING); + CONNECT ("exit-action-combobox", TERMINAL_PROFILE_EXIT_ACTION); + CONNECT ("font-selector", TERMINAL_PROFILE_FONT); + CONNECT ("foreground-colorpicker", TERMINAL_PROFILE_FOREGROUND_COLOR); + CONNECT ("image-radiobutton", TERMINAL_PROFILE_BACKGROUND_TYPE); + CONNECT ("login-shell-checkbutton", TERMINAL_PROFILE_LOGIN_SHELL); + CONNECT ("profile-name-entry", TERMINAL_PROFILE_VISIBLE_NAME); + CONNECT ("scrollback-lines-spinbutton", TERMINAL_PROFILE_SCROLLBACK_LINES); + CONNECT ("scrollback-unlimited-checkbutton", TERMINAL_PROFILE_SCROLLBACK_UNLIMITED); + CONNECT ("scroll-background-checkbutton", TERMINAL_PROFILE_SCROLL_BACKGROUND); + CONNECT ("scrollbar-position-combobox", TERMINAL_PROFILE_SCROLLBAR_POSITION); + CONNECT ("scroll-on-keystroke-checkbutton", TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE); + CONNECT ("scroll-on-output-checkbutton", TERMINAL_PROFILE_SCROLL_ON_OUTPUT); + CONNECT ("show-menubar-checkbutton", TERMINAL_PROFILE_DEFAULT_SHOW_MENUBAR); + CONNECT ("solid-radiobutton", TERMINAL_PROFILE_BACKGROUND_TYPE); + CONNECT ("system-font-checkbutton", TERMINAL_PROFILE_USE_SYSTEM_FONT); + CONNECT ("title-entry", TERMINAL_PROFILE_TITLE); + CONNECT ("title-mode-combobox", TERMINAL_PROFILE_TITLE_MODE); + CONNECT ("transparent-radiobutton", TERMINAL_PROFILE_BACKGROUND_TYPE); + CONNECT ("update-records-checkbutton", TERMINAL_PROFILE_UPDATE_RECORDS); + CONNECT ("use-custom-command-checkbutton", TERMINAL_PROFILE_USE_CUSTOM_COMMAND); + CONNECT ("use-custom-default-size-checkbutton", TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE); + CONNECT ("use-theme-colors-checkbutton", TERMINAL_PROFILE_USE_THEME_COLORS); + CONNECT ("word-chars-entry", TERMINAL_PROFILE_WORD_CHARS); + CONNECT_WITH_FLAGS ("bell-checkbutton", TERMINAL_PROFILE_SILENT_BELL, FLAG_INVERT_BOOL); + +#undef CONNECT +#undef CONNECT_WITH_FLAGS +#undef SET_ENUM_VALUE + + profile_notify_sensitivity_cb (profile, NULL, editor); + g_signal_connect (profile, "notify", + G_CALLBACK (profile_notify_sensitivity_cb), + editor); + g_signal_connect (profile, + "forgotten", + G_CALLBACK (profile_forgotten_cb), + editor); + + terminal_profile_editor_focus_widget (editor, widget_name); + + gtk_window_set_transient_for (GTK_WINDOW (editor), + GTK_WINDOW (transient_parent)); + gtk_window_present (GTK_WINDOW (editor)); +} diff --git a/src/profile-editor.h b/src/profile-editor.h new file mode 100644 index 0000000..2eec796 --- /dev/null +++ b/src/profile-editor.h @@ -0,0 +1,34 @@ +/* + * Copyright © 2002 Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TERMINAL_PROFILE_EDITOR_H +#define TERMINAL_PROFILE_EDITOR_H + +#include "terminal-profile.h" +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +void terminal_profile_edit (TerminalProfile *profile, + GtkWindow *transient_parent, + const char *widget_name); + +G_END_DECLS + +#endif /* TERMINAL_PROFILE_EDITOR_H */ diff --git a/src/profile-manager.glade b/src/profile-manager.glade new file mode 100644 index 0000000..1eb3931 --- /dev/null +++ b/src/profile-manager.glade @@ -0,0 +1,213 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.mate.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="profile-manager"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="title" translatable="yes">Profiles</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="default_width">400</property> + <property name="default_height">300</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="role">mate-terminal-profile-manager</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox5"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area5"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="helpbutton2"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-help</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-11</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="closebutton2"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-7</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox84"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox25"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkScrolledWindow" id="profiles-treeview-container"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVButtonBox" id="vbuttonbox1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_START</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkButton" id="new-profile-button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-new</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="edit-profile-button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-edit</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="delete-profile-button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-delete</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="default-profile-hbox"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="default-profile-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Profile used when launching a new terminal:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/src/profile-new-dialog.glade b/src/profile-new-dialog.glade new file mode 100644 index 0000000..1a22599 --- /dev/null +++ b/src/profile-new-dialog.glade @@ -0,0 +1,261 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.mate.org/glade-2.0.dtd"> + +<glade-interface> +<requires lib="mate"/> + +<widget class="GtkDialog" id="new-profile-dialog"> + <property name="border_width">5</property> + <property name="title" translatable="yes">New Profile</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox5"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area5"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="new-profile-cancel-button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="new-profile-create-button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-3</property> + + <child> + <widget class="GtkAlignment" id="alignment34"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox10"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="icon-1212"> + <property name="visible">True</property> + <property name="stock">gtk-apply</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="create-button"> + <property name="visible">True</property> + <property name="label" translatable="yes">C_reate</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox7"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkTable" id="new-profile-table"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkVBox" id="vbox77"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="new-profile-name-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Profile _name:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">new-profile-name-entry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="new-profile-name-entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + <property name="width_chars">14</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="new-profile-base-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Base on:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/src/profile-preferences.glade b/src/profile-preferences.glade new file mode 100644 index 0000000..09be1bc --- /dev/null +++ b/src/profile-preferences.glade @@ -0,0 +1,2814 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.mate.org/glade-2.0.dtd"> + +<glade-interface> +<requires lib="mate"/> + +<widget class="GtkDialog" id="profile-editor-dialog"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="title" translatable="yes">Profile Editor</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">True</property> + <property name="role">mate-terminal-profile-editor</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="helpbutton2"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-help</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-11</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="closebutton2"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-7</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkNotebook" id="profile-editor-notebook"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="show_tabs">True</property> + <property name="show_border">True</property> + <property name="tab_pos">GTK_POS_TOP</property> + <property name="scrollable">False</property> + <property name="enable_popup">False</property> + + <child> + <widget class="GtkVBox" id="vbox3"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox135"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="profile-name-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Profile name:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">4</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">profile-name-entry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="profile-name-entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="system-font-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Use the system fixed width font</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment10109"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="font-hbox"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="font-selector-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Font:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">font-selector</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkFontButton" id="font-selector"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="title" translatable="yes">Choose A Terminal Font</property> + <property name="show_style">True</property> + <property name="show_size">True</property> + <property name="use_font">True</property> + <property name="use_size">True</property> + <property name="focus_on_click">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="allow-bold-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Allow bold text</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="show-menubar-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Show _menubar by default in new terminals</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="bell-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Terminal _bell</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox140"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="label480"> + <property name="visible">True</property> + <property name="label" translatable="yes">Cursor _shape:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">cursor-shape-combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="cursor-shape-combobox"> + <property name="visible">True</property> + <property name="items" translatable="yes">Block +I-Beam +Underline</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox136"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="word-chars-entry-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Select-by-_word characters:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">word-chars-entry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="word-chars-entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="use-custom-default-size-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Use custom default terminal si_ze</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment10110"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="default-size-hbox"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="default-size-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Default size:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox143"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkSpinButton" id="default-size-columns-spinbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">False</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">80 1 1024 1 10 0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="default-size-columns-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">columns</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox142"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkSpinButton" id="default-size-rows-spinbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">False</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">24 1 1024 1 10 0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="default-size-rows-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">rows</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label32"> + <property name="visible">True</property> + <property name="label" translatable="yes">General</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox91"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">18</property> + + <child> + <widget class="GtkVBox" id="vbox79"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label33"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Title</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment10108"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox93"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox137"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="title-entry-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Initial _title:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">title-entry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="title-entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox138"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="title-mode-combobox-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">When terminal commands set their o_wn titles:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">title-mode-combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="title-mode-combobox"> + <property name="visible">True</property> + <property name="items" translatable="yes">Replace initial title +Append initial title +Prepend initial title +Keep initial title</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox80"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label36"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Command</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment10107"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox81"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkVBox" id="vbox92"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkCheckButton" id="login-shell-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Run command as a login shell</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="update-records-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Update login records when command is launched</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="use-custom-command-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Ru_n a custom command instead of my shell</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="custom-command-box"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox134"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="custom-command-entry-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Custom co_mmand:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">custom-command-entry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="custom-command-entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox27"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="exit-action-combobox-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">When command _exits:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">exit-action-combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="exit-action-combobox"> + <property name="visible">True</property> + <property name="items" translatable="yes">Exit the terminal +Restart the command +Hold the terminal open</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label38"> + <property name="visible">True</property> + <property name="label" translatable="yes">Title and Command</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox90"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">18</property> + + <child> + <widget class="GtkVBox" id="vbox82"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label39"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Foreground, Background, Bold and Underline</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment10105"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox94"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkCheckButton" id="use-theme-colors-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Use colors from system theme</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox145"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="color-scheme-combobox-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Built-in sche_mes:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">color-scheme-combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="color-scheme-combobox"> + <property name="visible">True</property> + <property name="items" translatable="yes">Custom</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox146"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">18</property> + + <child> + <widget class="GtkTable" id="table31"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="foreground-colorpicker-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Text color:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">foreground-colorpicker</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="background-colorpicker-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Background color:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">background-colorpicker</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="background-colorpicker"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="title" translatable="yes">Choose Terminal Background Color</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="foreground-colorpicker"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="title" translatable="yes">Choose Terminal Text Color</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="underline-colorpicker-label"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkColorButton" id="bold-colorpicker"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label481"> + <property name="label" translatable="yes">_Underline color:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">underline-colorpicker</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="underline-colorpicker"> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="underline-color-same-as-fg-checkbox"> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Same as text color</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="bold-colorpicker-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Bol_d color:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">bold-colorpicker</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">shrink|fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="bold-color-same-as-fg-checkbox"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Same as text color</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox83"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label42"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Palette</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment10106"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkTable" id="table25"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="palette-optionmenu-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Built-in _schemes:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">palette-combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="palette-table"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">8</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-1"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-2"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-3"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-4"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-5"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">4</property> + <property name="right_attach">5</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-6"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">5</property> + <property name="right_attach">6</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-8"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">7</property> + <property name="right_attach">8</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-9"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-10"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-12"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-11"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-13"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">4</property> + <property name="right_attach">5</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-16"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">7</property> + <property name="right_attach">8</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-14"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">5</property> + <property name="right_attach">6</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-7"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">6</property> + <property name="right_attach">7</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options"></property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="palette-colorpicker-15"> + <property name="visible">True</property> + <property name="tooltip">dummy</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">6</property> + <property name="right_attach">7</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label44"> + <property name="visible">True</property> + <property name="label" translatable="yes"><small><i><b>Note:</b> Terminal applications have these colors available to them.</i></small></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label43"> + <property name="visible">True</property> + <property name="label" translatable="yes">Color p_alette:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">palette-colorpicker-1</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="palette-combobox"> + <property name="visible">True</property> + <property name="items" translatable="yes">Tango +Linux console +XTerm +Rxvt +Custom</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label45"> + <property name="visible">True</property> + <property name="label" translatable="yes">Colors</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox86"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkRadioButton" id="solid-radiobutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Solid color</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox87"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkRadioButton" id="image-radiobutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Background image</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <property name="group">solid-radiobutton</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment10103"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox89"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkHBox" id="hbox2"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="background-image-filechooser-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Image _file:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">background-image-filechooser</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkFileChooserButton" id="background-image-filechooser"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="title" translatable="yes">Select Background Image</property> + <property name="action">GTK_FILE_CHOOSER_ACTION_OPEN</property> + <property name="local_only">True</property> + <property name="show_hidden">False</property> + <property name="do_overwrite_confirmation">False</property> + <property name="width_chars">-1</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="scroll-background-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Background image _scrolls</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkRadioButton" id="transparent-radiobutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Transparent background</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <property name="group">solid-radiobutton</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="darken-background-vbox"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="darken-background-scale-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">S_hade transparent or image background:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">darken-background-scale</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">1</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox6"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label64"> + <property name="visible">True</property> + <property name="label" translatable="yes"><small><i>None</i></small></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">6</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHScale" id="darken-background-scale"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="draw_value">False</property> + <property name="value_pos">GTK_POS_BOTTOM</property> + <property name="digits">2</property> + <property name="update_policy">GTK_UPDATE_DELAYED</property> + <property name="inverted">False</property> + <property name="adjustment">0.10000000149 0 1 0.00999999977648 0.10000000149 0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label63"> + <property name="visible">True</property> + <property name="label" translatable="yes"><small><i>Maximum</i></small></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">1</property> + <property name="yalign">0.5</property> + <property name="xpad">6</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">1</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label479"> + <property name="visible">True</property> + <property name="label" translatable="yes">Background</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="table27"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="n_rows">5</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="scrollbar-position-combobox-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Scrollbar is:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">scrollbar-position-combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="scrollback-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Scroll_back:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">scrollback-lines-spinbutton</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="scroll-on-keystroke-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Scroll on _keystroke</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="scroll-on-output-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Scroll on _output</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="scrollback-unlimited-checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Unlimited</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox139"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkComboBox" id="scrollbar-position-combobox"> + <property name="visible">True</property> + <property name="items" translatable="yes">On the left side +On the right side +Disabled</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="scrollback-box"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkSpinButton" id="scrollback-lines-spinbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">True</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">1 1 2147483647 1 100 0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="scrollback-lines-spinbutton-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">lines</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">scrollback-lines-spinbutton</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">fill</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label60"> + <property name="visible">True</property> + <property name="label" translatable="yes">Scrolling</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox4"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="label51"> + <property name="visible">True</property> + <property name="label" translatable="yes"><small><i><b>Note:</b> These options may cause some applications to behave incorrectly. They are only here to allow you to work around certain applications and operating systems that expect different terminal behavior.</i></small></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">7.45058015283e-09</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="table30"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="delete-binding-combobox-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Delete key generates:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">delete-binding-combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="backspace-binding-combobox-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Backspace key generates:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">backspace-binding-combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="backspace-binding-combobox"> + <property name="visible">True</property> + <property name="items" translatable="yes">Automatic +Control-H +ASCII DEL +Escape sequence +TTY Erase</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="delete-binding-combobox"> + <property name="visible">True</property> + <property name="items" translatable="yes">Automatic +Control-H +ASCII DEL +Escape sequence +TTY Erase</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHButtonBox" id="hbuttonbox1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_START</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkButton" id="reset-compat-defaults-button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Reset Compatibility Options to Defaults</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label54"> + <property name="visible">True</property> + <property name="label" translatable="yes">Compatibility</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/src/skey-challenge.glade b/src/skey-challenge.glade new file mode 100644 index 0000000..6947783 --- /dev/null +++ b/src/skey-challenge.glade @@ -0,0 +1,198 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.mate.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="skey-dialog"> + <property name="border_width">5</property> + <property name="title" translatable="yes"></property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">True</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox4"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area4"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="cancelbutton1"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="skey-ok-button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox4"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkImage" id="skey-image"> + <property name="visible">True</property> + <property name="stock">gtk-dialog-authentication</property> + <property name="icon_size">6</property> + <property name="xalign">0.5</property> + <property name="yalign">0</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox6"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="text-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">S/Key Challenge Response</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox5"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="label62"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Password:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">skey-entry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="skey-entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">False</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/src/skey-popup.c b/src/skey-popup.c new file mode 100644 index 0000000..553e035 --- /dev/null +++ b/src/skey-popup.c @@ -0,0 +1,228 @@ +/* + * Copyright © 2002 Jonathan Blandford <[email protected]> + * Copyright © 2008 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "terminal-intl.h" + +#include "terminal-util.h" +#include "terminal-screen.h" +#include "skey-popup.h" +#include "skey/skey.h" +#include <stdlib.h> +#include <string.h> + +#define SKEY_PREFIX "s/key " +#define OTP_PREFIX "otp-" + +typedef struct { + TerminalScreen *screen; + char *seed; + int seq; + int hash; +} SkeyData; + +static void +skey_data_free (SkeyData *data) +{ + g_free (data->seed); + g_free (data); +} + +static gboolean +extract_seq_and_seed (const gchar *skey_match, + gint *seq, + gchar **seed) +{ + gchar *end_ptr = NULL; + + /* FIXME: use g_ascii_strtoll */ + *seq = strtol (skey_match + strlen (SKEY_PREFIX), &end_ptr, 0); + + if (end_ptr == NULL || *end_ptr == '\000') + return FALSE; + + *seed = g_strdup (end_ptr + 1); + + return TRUE; +} + +static gboolean +extract_hash_seq_and_seed (const gchar *otp_match, + gint *hash, + gint *seq, + gchar **seed) +{ + gchar *end_ptr = NULL; + const gchar *p = otp_match + strlen (OTP_PREFIX); + gint len = 3; + + if (strncmp (p, "md4", 3) == 0) + *hash = MD4; + else if (strncmp (p, "md5", 3) == 0) + *hash = MD5; + else if (strncmp (p, "sha1", 4) == 0) + { + *hash = SHA1; + len++; + } + else + return FALSE; + + p += len; + + /* RFC mandates the following skipping */ + while (*p == ' ' || *p == '\t') + { + if (*p == '\0') + return FALSE; + + p++; + } + + /* FIXME: use g_ascii_strtoll */ + *seq = strtol (p, &end_ptr, 0); + + if (end_ptr == NULL || *end_ptr == '\000') + return FALSE; + + p = end_ptr; + + while (*p == ' ' || *p == '\t') + { + if (*p == '\0') + return FALSE; + p++; + } + + *seed = g_strdup (p); + return TRUE; +} + +static void +skey_challenge_response_cb (GtkWidget *dialog, + int response_id, + SkeyData *data) +{ + if (response_id == GTK_RESPONSE_OK) + { + GtkWidget *entry; + const char *password; + char *response; + + entry = g_object_get_data (G_OBJECT (dialog), "skey-entry"); + password = gtk_entry_get_text (GTK_ENTRY (entry)); + + /* FIXME: fix skey to use g_malloc */ + response = skey (data->hash, data->seq, data->seed, password); + if (response) + { + VteTerminal *vte_terminal = VTE_TERMINAL (data->screen); + static const char newline[2] = "\n"; + + vte_terminal_feed_child (vte_terminal, response, strlen (response)); + vte_terminal_feed_child (vte_terminal, newline, strlen (newline)); + free (response); + } + } + + gtk_widget_destroy (dialog); +} + +void +terminal_skey_do_popup (GtkWindow *window, + TerminalScreen *screen, + const gchar *skey_match) +{ + GtkWidget *dialog, *label, *entry, *ok_button; + char *title_text; + char *seed; + int seq; + int hash = MD5; + SkeyData *data; + + if (strncmp (SKEY_PREFIX, skey_match, strlen (SKEY_PREFIX)) == 0) + { + if (!extract_seq_and_seed (skey_match, &seq, &seed)) + { + terminal_util_show_error_dialog (window, NULL, NULL, + _("The text you clicked on doesn't " + "seem to be a valid S/Key " + "challenge.")); + return; + } + } + else + { + if (!extract_hash_seq_and_seed (skey_match, &hash, &seq, &seed)) + { + terminal_util_show_error_dialog (window, NULL, NULL, + _("The text you clicked on doesn't " + "seem to be a valid OTP " + "challenge.")); + return; + } + } + + if (!terminal_util_load_builder_file ("skey-challenge.ui", + "skey-dialog", &dialog, + "skey-entry", &entry, + "text-label", &label, + "skey-ok-button", &ok_button, + NULL)) + { + g_free (seed); + return; + } + + title_text = g_strdup_printf ("<big><b>%s</b></big>", + gtk_label_get_text (GTK_LABEL (label))); + gtk_label_set_label (GTK_LABEL (label), title_text); + g_free (title_text); + + g_object_set_data (G_OBJECT (dialog), "skey-entry", entry); + + gtk_widget_grab_focus (entry); + gtk_widget_grab_default (ok_button); + gtk_entry_set_text (GTK_ENTRY (entry), ""); + + gtk_window_set_transient_for (GTK_WINDOW (dialog), window); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + /* FIXME: make this dialogue close if the screen closes! */ + + data = g_new (SkeyData, 1); + data->hash = hash; + data->seq = seq; + data->seed = seed; + data->screen = screen; + + g_signal_connect_data (dialog, "response", + G_CALLBACK (skey_challenge_response_cb), + data, (GClosureNotify) skey_data_free, 0); + g_signal_connect (dialog, "delete-event", + G_CALLBACK (terminal_util_dialog_response_on_delete), NULL); + + gtk_window_present (GTK_WINDOW (dialog)); +} diff --git a/src/skey-popup.h b/src/skey-popup.h new file mode 100644 index 0000000..f4847eb --- /dev/null +++ b/src/skey-popup.h @@ -0,0 +1,35 @@ +/* + * Copyright © 2002 Jonathan Blandford <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef SKEY_POPUP_H +#define SKEY_POPUP_H + +#include <gtk/gtk.h> + +#include "terminal-screen.h" + +G_BEGIN_DECLS + +void terminal_skey_do_popup (GtkWindow *window, + TerminalScreen *screen, + const gchar *skey_match); + +G_END_DECLS + +#endif /* SKEY_POPUP_H */ diff --git a/src/skey/Makefile.am b/src/skey/Makefile.am new file mode 100644 index 0000000..0402507 --- /dev/null +++ b/src/skey/Makefile.am @@ -0,0 +1,60 @@ +NULL = + +noinst_LTLIBRARIES = libskey.la + +libskey_la_SOURCES = \ + btoe.c \ + btoe.h \ + skey.h \ + skey.c \ + skeyutil.h\ + skeyutil.c\ + md4.c \ + md4.h \ + md5.c \ + md5.h \ + sha1.h \ + sha1.c \ + $(NULL) + +libskey_la_CPPFLAGS = \ + -I$(srcdir)/.. \ + -DG_DISABLE_SINGLE_INCLUDES \ + $(DISABLE_DEPRECATED) \ + $(AM_CPPFLAGS) + +libskey_la_CFLAGS = \ + $(TERM_CFLAGS) \ + $(WARN_CFLAGS) \ + $(AM_CFLASG) + +libskey_la_LDFLAGS = + +libskey_la_LIBADD = \ + $(TERM_LIBS) + +check_PROGRAMS = testskey + +testskey_SOURCES = \ + test.c \ + $(NULL) + +testskey_CPPFLAGS = \ + -I$(srcdir)/.. \ + $(DISABLE_DEPRECATED) \ + $(AM_CPPFLAGS) + +testskey_CFLAGS = \ + $(TERM_CFLAGS) \ + $(WARN_CFLAGS) \ + $(AM_CFLAGS) + +testskey_LDFLAGS = + +testskey_LDADD = \ + libskey.la \ + $(TERM_LIBS) + +TESTS = testskey + +-include $(top_srcdir)/git.mk diff --git a/src/skey/Makefile.in b/src/skey/Makefile.in new file mode 100644 index 0000000..bfe467c --- /dev/null +++ b/src/skey/Makefile.in @@ -0,0 +1,777 @@ +# 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@ +check_PROGRAMS = testskey$(EXEEXT) +TESTS = testskey$(EXEEXT) +subdir = src/skey +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(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 = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libskey_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am__objects_1 = +am_libskey_la_OBJECTS = libskey_la-btoe.lo libskey_la-skey.lo \ + libskey_la-skeyutil.lo libskey_la-md4.lo libskey_la-md5.lo \ + libskey_la-sha1.lo $(am__objects_1) +libskey_la_OBJECTS = $(am_libskey_la_OBJECTS) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +libskey_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libskey_la_CFLAGS) \ + $(CFLAGS) $(libskey_la_LDFLAGS) $(LDFLAGS) -o $@ +am_testskey_OBJECTS = testskey-test.$(OBJEXT) $(am__objects_1) +testskey_OBJECTS = $(am_testskey_OBJECTS) +testskey_DEPENDENCIES = libskey.la $(am__DEPENDENCIES_1) +testskey_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(testskey_CFLAGS) \ + $(CFLAGS) $(testskey_LDFLAGS) $(LDFLAGS) -o $@ +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 = $(libskey_la_SOURCES) $(testskey_SOURCES) +DIST_SOURCES = $(libskey_la_SOURCES) $(testskey_SOURCES) +ETAGS = etags +CTAGS = ctags +am__tty_colors = \ +red=; grn=; lgn=; blu=; std= +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@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@ +DLLTOOL = @DLLTOOL@ +DOC_USER_FORMATS = @DOC_USER_FORMATS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GREP = @GREP@ +GTK_BUILDER_CONVERT = @GTK_BUILDER_CONVERT@ +HELP_DIR = @HELP_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLLIBS = @INTLLIBS@ +INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@ +INTLTOOL_MERGE = @INTLTOOL_MERGE@ +INTLTOOL_PERL = @INTLTOOL_PERL@ +INTLTOOL_UPDATE = @INTLTOOL_UPDATE@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +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_SCHEMA_CONFIG_SOURCE = @MATECONF_SCHEMA_CONFIG_SOURCE@ +MATECONF_SCHEMA_FILE_DIR = @MATECONF_SCHEMA_FILE_DIR@ +MKDIR_P = @MKDIR_P@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGFMT_OPTS = @MSGFMT_OPTS@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OMF_DIR = @OMF_DIR@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POFILES = @POFILES@ +POSUB = @POSUB@ +PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ +PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SMCLIENT_CFLAGS = @SMCLIENT_CFLAGS@ +SMCLIENT_LIBS = @SMCLIENT_LIBS@ +STRIP = @STRIP@ +TERMINAL_API_VERSION = @TERMINAL_API_VERSION@ +TERMINAL_MAJOR_VERSION = @TERMINAL_MAJOR_VERSION@ +TERMINAL_MICRO_VERSION = @TERMINAL_MICRO_VERSION@ +TERMINAL_MINOR_VERSION = @TERMINAL_MINOR_VERSION@ +TERM_CFLAGS = @TERM_CFLAGS@ +TERM_LIBS = @TERM_LIBS@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +XGETTEXT = @XGETTEXT@ +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@ +NULL = +noinst_LTLIBRARIES = libskey.la +libskey_la_SOURCES = \ + btoe.c \ + btoe.h \ + skey.h \ + skey.c \ + skeyutil.h\ + skeyutil.c\ + md4.c \ + md4.h \ + md5.c \ + md5.h \ + sha1.h \ + sha1.c \ + $(NULL) + +libskey_la_CPPFLAGS = \ + -I$(srcdir)/.. \ + -DG_DISABLE_SINGLE_INCLUDES \ + $(DISABLE_DEPRECATED) \ + $(AM_CPPFLAGS) + +libskey_la_CFLAGS = \ + $(TERM_CFLAGS) \ + $(WARN_CFLAGS) \ + $(AM_CFLASG) + +libskey_la_LDFLAGS = +libskey_la_LIBADD = \ + $(TERM_LIBS) + +testskey_SOURCES = \ + test.c \ + $(NULL) + +testskey_CPPFLAGS = \ + -I$(srcdir)/.. \ + $(DISABLE_DEPRECATED) \ + $(AM_CPPFLAGS) + +testskey_CFLAGS = \ + $(TERM_CFLAGS) \ + $(WARN_CFLAGS) \ + $(AM_CFLAGS) + +testskey_LDFLAGS = +testskey_LDADD = \ + libskey.la \ + $(TERM_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) --foreign src/skey/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/skey/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): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libskey.la: $(libskey_la_OBJECTS) $(libskey_la_DEPENDENCIES) + $(AM_V_CCLD)$(libskey_la_LINK) $(libskey_la_OBJECTS) $(libskey_la_LIBADD) $(LIBS) + +clean-checkPROGRAMS: + @list='$(check_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 +testskey$(EXEEXT): $(testskey_OBJECTS) $(testskey_DEPENDENCIES) + @rm -f testskey$(EXEEXT) + $(AM_V_CCLD)$(testskey_LINK) $(testskey_OBJECTS) $(testskey_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libskey_la-btoe.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libskey_la-md4.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libskey_la-md5.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libskey_la-sha1.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libskey_la-skey.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libskey_la-skeyutil.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testskey-test.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 $@ $< + +libskey_la-btoe.lo: btoe.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -MT libskey_la-btoe.lo -MD -MP -MF $(DEPDIR)/libskey_la-btoe.Tpo -c -o libskey_la-btoe.lo `test -f 'btoe.c' || echo '$(srcdir)/'`btoe.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libskey_la-btoe.Tpo $(DEPDIR)/libskey_la-btoe.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='btoe.c' object='libskey_la-btoe.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -c -o libskey_la-btoe.lo `test -f 'btoe.c' || echo '$(srcdir)/'`btoe.c + +libskey_la-skey.lo: skey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -MT libskey_la-skey.lo -MD -MP -MF $(DEPDIR)/libskey_la-skey.Tpo -c -o libskey_la-skey.lo `test -f 'skey.c' || echo '$(srcdir)/'`skey.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libskey_la-skey.Tpo $(DEPDIR)/libskey_la-skey.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='skey.c' object='libskey_la-skey.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -c -o libskey_la-skey.lo `test -f 'skey.c' || echo '$(srcdir)/'`skey.c + +libskey_la-skeyutil.lo: skeyutil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -MT libskey_la-skeyutil.lo -MD -MP -MF $(DEPDIR)/libskey_la-skeyutil.Tpo -c -o libskey_la-skeyutil.lo `test -f 'skeyutil.c' || echo '$(srcdir)/'`skeyutil.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libskey_la-skeyutil.Tpo $(DEPDIR)/libskey_la-skeyutil.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='skeyutil.c' object='libskey_la-skeyutil.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -c -o libskey_la-skeyutil.lo `test -f 'skeyutil.c' || echo '$(srcdir)/'`skeyutil.c + +libskey_la-md4.lo: md4.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -MT libskey_la-md4.lo -MD -MP -MF $(DEPDIR)/libskey_la-md4.Tpo -c -o libskey_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libskey_la-md4.Tpo $(DEPDIR)/libskey_la-md4.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md4.c' object='libskey_la-md4.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -c -o libskey_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c + +libskey_la-md5.lo: md5.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -MT libskey_la-md5.lo -MD -MP -MF $(DEPDIR)/libskey_la-md5.Tpo -c -o libskey_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libskey_la-md5.Tpo $(DEPDIR)/libskey_la-md5.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5.c' object='libskey_la-md5.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -c -o libskey_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c + +libskey_la-sha1.lo: sha1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -MT libskey_la-sha1.lo -MD -MP -MF $(DEPDIR)/libskey_la-sha1.Tpo -c -o libskey_la-sha1.lo `test -f 'sha1.c' || echo '$(srcdir)/'`sha1.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libskey_la-sha1.Tpo $(DEPDIR)/libskey_la-sha1.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sha1.c' object='libskey_la-sha1.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libskey_la_CPPFLAGS) $(CPPFLAGS) $(libskey_la_CFLAGS) $(CFLAGS) -c -o libskey_la-sha1.lo `test -f 'sha1.c' || echo '$(srcdir)/'`sha1.c + +testskey-test.o: test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testskey_CPPFLAGS) $(CPPFLAGS) $(testskey_CFLAGS) $(CFLAGS) -MT testskey-test.o -MD -MP -MF $(DEPDIR)/testskey-test.Tpo -c -o testskey-test.o `test -f 'test.c' || echo '$(srcdir)/'`test.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/testskey-test.Tpo $(DEPDIR)/testskey-test.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test.c' object='testskey-test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testskey_CPPFLAGS) $(CPPFLAGS) $(testskey_CFLAGS) $(CFLAGS) -c -o testskey-test.o `test -f 'test.c' || echo '$(srcdir)/'`test.c + +testskey-test.obj: test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testskey_CPPFLAGS) $(CPPFLAGS) $(testskey_CFLAGS) $(CFLAGS) -MT testskey-test.obj -MD -MP -MF $(DEPDIR)/testskey-test.Tpo -c -o testskey-test.obj `if test -f 'test.c'; then $(CYGPATH_W) 'test.c'; else $(CYGPATH_W) '$(srcdir)/test.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/testskey-test.Tpo $(DEPDIR)/testskey-test.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test.c' object='testskey-test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(testskey_CPPFLAGS) $(CPPFLAGS) $(testskey_CFLAGS) $(CFLAGS) -c -o testskey-test.obj `if test -f 'test.c'; then $(CYGPATH_W) 'test.c'; else $(CYGPATH_W) '$(srcdir)/test.c'; fi` + +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 + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + echo "$$grn$$dashes"; \ + else \ + echo "$$red$$dashes"; \ + fi; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes$$std"; \ + test "$$failed" -eq 0; \ + else :; fi + +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 + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +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-checkPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +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: + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \ + clean-checkPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-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 + + +-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/src/skey/btoe.c b/src/skey/btoe.c new file mode 100644 index 0000000..f9bd459 --- /dev/null +++ b/src/skey/btoe.c @@ -0,0 +1,304 @@ +/* + * This code is imported from Bollcore's S/KEY + some simple glib adaptations + * (See rfc2289 and rfc1760) + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <glib.h> + +#include "skey.h" +#include "btoe.h" + +static guint32 extract (char *s, int start, int length); + +/* Dictionary for integer-word translations */ +static const char Wp[2048][4] = { "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", +"AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN", "ANA", +"AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK", +"ARM", "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", "AVE", +"AWE", "AWK", "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM", +"BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", "BET", +"BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO", +"BOP", "BOW", "BOY", "BUB", "BUD", "BUG", "BUM", "BUN", "BUS", "BUT", +"BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT", +"CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", "COY", +"CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN", +"DAR", "DAY", "DEE", "DEL", "DEN", "DES", "DEW", "DID", "DIE", "DIG", +"DIN", "DIP", "DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB", +"DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", "EGG", "EGO", +"ELI", "ELK", "ELM", "ELY", "EM", "END", "EST", "ETC", "EVA", "EVE", +"EWE", "EYE", "FAD", "FAN", "FAR", "FAT", "FAY", "FED", "FEE", "FEW", +"FIB", "FIG", "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", +"FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM", "GAP", +"GAS", "GAY", "GEE", "GEL", "GEM", "GET", "GIG", "GIL", "GIN", "GO", +"GOT", "GUM", "GUN", "GUS", "GUT", "GUY", "GYM", "GYP", "HA", "HAD", +"HAL", "HAM", "HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM", +"HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", "HIS", "HIT", +"HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT", "HOW", "HUB", "HUE", +"HUG", "HUH", "HUM", "HUT", "I", "ICY", "IDA", "IF", "IKE", "ILL", +"INK", "INN", "IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", "ITS", +"IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", "JAY", "JET", "JIG", +"JIM", "JO", "JOB", "JOE", "JOG", "JOT", "JOY", "JUG", "JUT", "KAY", +"KEG", "KEN", "KEY", "KID", "KIM", "KIN", "KIT", "LA", "LAB", "LAC", +"LAD", "LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", "LEG", +"LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", "LIP", "LIT", "LO", +"LOB", "LOG", "LOP", "LOS", "LOT", "LOU", "LOW", "LOY", "LUG", "LYE", +"MA", "MAC", "MAD", "MAE", "MAN", "MAO", "MAP", "MAT", "MAW", "MAY", +"ME", "MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT", "MOB", +"MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD", "MUG", "MUM", +"MY", "NAB", "NAG", "NAN", "NAP", "NAT", "NAY", "NE", "NED", "NEE", +"NET", "NEW", "NIB", "NIL", "NIP", "NIT", "NO", "NOB", "NOD", "NON", +"NOR", "NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF", "OAK", +"OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", "OH", "OIL", "OK", +"OLD", "ON", "ONE", "OR", "ORB", "ORE", "ORR", "OS", "OTT", "OUR", +"OUT", "OVA", "OW", "OWE", "OWL", "OWN", "OX", "PA", "PAD", "PAL", +"PAM", "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG", "PEN", +"PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN", "PIT", "PLY", +"PO", "POD", "POE", "POP", "POT", "POW", "PRO", "PRY", "PUB", "PUG", +"PUN", "PUP", "PUT", "QUO", "RAG", "RAM", "RAN", "RAP", "RAT", "RAW", +"RAY", "REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM", "RIO", +"RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY", "RUB", "RUE", +"RUG", "RUM", "RUN", "RYE", "SAC", "SAD", "SAG", "SAL", "SAM", "SAN", +"SAP", "SAT", "SAW", "SAY", "SEA", "SEC", "SEE", "SEN", "SET", "SEW", +"SHE", "SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", "SLY", +"SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", "SPA", "SPY", "SUB", +"SUD", "SUE", "SUM", "SUN", "SUP", "TAB", "TAD", "TAG", "TAN", "TAP", +"TAR", "TEA", "TED", "TEE", "TEN", "THE", "THY", "TIC", "TIE", "TIM", +"TIN", "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", "TOW", +"TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", "UN", "UP", "US", +"USE", "VAN", "VAT", "VET", "VIE", "WAD", "WAG", "WAR", "WAS", "WAY", +"WE", "WEB", "WED", "WEE", "WET", "WHO", "WHY", "WIN", "WIT", "WOK", +"WON", "WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE", "YEA", +"YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", "ABUT", "ACHE", +"ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", "ADDS", "ADEN", "AFAR", +"AFRO", "AGEE", "AHEM", "AHOY", "AIDA", "AIDE", "AIDS", "AIRY", "AJAR", +"AKIN", "ALAN", "ALEC", "ALGA", "ALIA", "ALLY", "ALMA", "ALOE", "ALSO", +"ALTO", "ALUM", "ALVA", "AMEN", "AMES", "AMID", "AMMO", "AMOK", "AMOS", +"AMRA", "ANDY", "ANEW", "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", +"ARCH", "AREA", "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", +"ATOM", "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", "AVOW", +"AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", "BAIL", "BAIT", +"BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", "BALM", "BAND", "BANE", +"BANG", "BANK", "BARB", "BARD", "BARE", "BARK", "BARN", "BARR", "BASE", +"BASH", "BASK", "BASS", "BATE", "BATH", "BAWD", "BAWL", "BEAD", "BEAK", +"BEAM", "BEAN", "BEAR", "BEAT", "BEAU", "BECK", "BEEF", "BEEN", "BEER", +"BEET", "BELA", "BELL", "BELT", "BEND", "BENT", "BERG", "BERN", "BERT", +"BESS", "BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE", "BIEN", "BILE", +"BILK", "BILL", "BIND", "BING", "BIRD", "BITE", "BITS", "BLAB", "BLAT", +"BLED", "BLEW", "BLOB", "BLOC", "BLOT", "BLOW", "BLUE", "BLUM", "BLUR", +"BOAR", "BOAT", "BOCA", "BOCK", "BODE", "BODY", "BOGY", "BOHR", "BOIL", +"BOLD", "BOLO", "BOLT", "BOMB", "BONA", "BOND", "BONE", "BONG", "BONN", +"BONY", "BOOK", "BOOM", "BOON", "BOOT", "BORE", "BORG", "BORN", "BOSE", +"BOSS", "BOTH", "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN", +"BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", "BUFF", +"BULB", "BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG", "BURL", "BURN", +"BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST", "BUSY", "BYTE", "CADY", +"CAFE", "CAGE", "CAIN", "CAKE", "CALF", "CALL", "CALM", "CAME", "CANE", +"CANT", "CARD", "CARE", "CARL", "CARR", "CART", "CASE", "CASH", "CASK", +"CAST", "CAVE", "CEIL", "CELL", "CENT", "CERN", "CHAD", "CHAR", "CHAT", +"CHAW", "CHEF", "CHEN", "CHEW", "CHIC", "CHIN", "CHOU", "CHOW", "CHUB", +"CHUG", "CHUM", "CITE", "CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY", +"CLOD", "CLOG", "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", "COCK", +"COCO", "CODA", "CODE", "CODY", "COED", "COIL", "COIN", "COKE", "COLA", +"COLD", "COLT", "COMA", "COMB", "COME", "COOK", "COOL", "COON", "COOT", +"CORD", "CORE", "CORK", "CORN", "COST", "COVE", "COWL", "CRAB", "CRAG", +"CRAM", "CRAY", "CREW", "CRIB", "CROW", "CRUD", "CUBA", "CUBE", "CUFF", +"CULL", "CULT", "CUNY", "CURB", "CURD", "CURE", "CURL", "CURT", "CUTS", +"DADE", "DALE", "DAME", "DANA", "DANE", "DANG", "DANK", "DARE", "DARK", +"DARN", "DART", "DASH", "DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS", +"DEAD", "DEAF", "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", "DEEM", +"DEER", "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", "DIAL", "DICE", +"DIED", "DIET", "DIME", "DINE", "DING", "DINT", "DIRE", "DIRT", "DISC", +"DISH", "DISK", "DIVE", "DOCK", "DOES", "DOLE", "DOLL", "DOLT", "DOME", +"DONE", "DOOM", "DOOR", "DORA", "DOSE", "DOTE", "DOUG", "DOUR", "DOVE", +"DOWN", "DRAB", "DRAG", "DRAM", "DRAW", "DREW", "DRUB", "DRUG", "DRUM", +"DUAL", "DUCK", "DUCT", "DUEL", "DUET", "DUKE", "DULL", "DUMB", "DUNE", +"DUNK", "DUSK", "DUST", "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", +"EASY", "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT", "EDNA", +"EGAN", "ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", "EMMA", "ENDS", +"ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED", "FACE", "FACT", "FADE", +"FAIL", "FAIN", "FAIR", "FAKE", "FALL", "FAME", "FANG", "FARM", "FAST", +"FATE", "FAWN", "FEAR", "FEAT", "FEED", "FEEL", "FEET", "FELL", "FELT", +"FEND", "FERN", "FEST", "FEUD", "FIEF", "FIGS", "FILE", "FILL", "FILM", +"FIND", "FINE", "FINK", "FIRE", "FIRM", "FISH", "FISK", "FIST", "FITS", +"FIVE", "FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW", +"FLIT", "FLOC", "FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM", "FOGY", +"FOIL", "FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL", "FOOT", "FORD", +"FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL", "FOUR", "FOWL", "FRAU", +"FRAY", "FRED", "FREE", "FRET", "FREY", "FROG", "FROM", "FUEL", "FULL", +"FUME", "FUND", "FUNK", "FURY", "FUSE", "FUSS", "GAFF", "GAGE", "GAIL", +"GAIN", "GAIT", "GALA", "GALE", "GALL", "GALT", "GAME", "GANG", "GARB", +"GARY", "GASH", "GATE", "GAUL", "GAUR", "GAVE", "GAWK", "GEAR", "GELD", +"GENE", "GENT", "GERM", "GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT", +"GINA", "GIRD", "GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN", "GLIB", +"GLOB", "GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", "GOAL", "GOAT", +"GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", "GOOD", "GOOF", "GORE", +"GORY", "GOSH", "GOUT", "GOWN", "GRAB", "GRAD", "GRAY", "GREG", "GREW", +"GREY", "GRID", "GRIM", "GRIN", "GRIT", "GROW", "GRUB", "GULF", "GULL", +"GUNK", "GURU", "GUSH", "GUST", "GWEN", "GWYN", "HAAG", "HAAS", "HACK", +"HAIL", "HAIR", "HALE", "HALF", "HALL", "HALO", "HALT", "HAND", "HANG", +"HANK", "HANS", "HARD", "HARK", "HARM", "HART", "HASH", "HAST", "HATE", +"HATH", "HAUL", "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", "HEAT", +"HEBE", "HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL", "HELM", "HERB", +"HERD", "HERE", "HERO", "HERS", "HESS", "HEWN", "HICK", "HIDE", "HIGH", +"HIKE", "HILL", "HILT", "HIND", "HINT", "HIRE", "HISS", "HIVE", "HOBO", +"HOCK", "HOFF", "HOLD", "HOLE", "HOLM", "HOLT", "HOME", "HONE", "HONK", +"HOOD", "HOOF", "HOOK", "HOOT", "HORN", "HOSE", "HOST", "HOUR", "HOVE", +"HOWE", "HOWL", "HOYT", "HUCK", "HUED", "HUFF", "HUGE", "HUGH", "HUGO", +"HULK", "HULL", "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE", +"HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", "INTO", +"IONS", "IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE", "ITCH", "ITEM", +"IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE", "JAVA", "JEAN", "JEFF", +"JERK", "JESS", "JEST", "JIBE", "JILL", "JILT", "JIVE", "JOAN", "JOBS", +"JOCK", "JOEL", "JOEY", "JOHN", "JOIN", "JOKE", "JOLT", "JOVE", "JUDD", +"JUDE", "JUDO", "JUDY", "JUJU", "JUKE", "JULY", "JUNE", "JUNK", "JUNO", +"JURY", "JUST", "JUTE", "KAHN", "KALE", "KANE", "KANT", "KARL", "KATE", +"KEEL", "KEEN", "KENO", "KENT", "KERN", "KERR", "KEYS", "KICK", "KILL", +"KIND", "KING", "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", "KNIT", +"KNOB", "KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD", "KURT", "KYLE", +"LACE", "LACK", "LACY", "LADY", "LAID", "LAIN", "LAIR", "LAKE", "LAMB", +"LAME", "LAND", "LANE", "LANG", "LARD", "LARK", "LASS", "LAST", "LATE", +"LAUD", "LAVA", "LAWN", "LAWS", "LAYS", "LEAD", "LEAF", "LEAK", "LEAN", +"LEAR", "LEEK", "LEER", "LEFT", "LEND", "LENS", "LENT", "LEON", "LESK", +"LESS", "LEST", "LETS", "LIAR", "LICE", "LICK", "LIED", "LIEN", "LIES", +"LIEU", "LIFE", "LIFT", "LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB", +"LIME", "LIND", "LINE", "LINK", "LINT", "LION", "LISA", "LIST", "LIVE", +"LOAD", "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", "LOIS", "LOLA", +"LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD", "LORE", "LOSE", "LOSS", +"LOST", "LOUD", "LOVE", "LOWE", "LUCK", "LUCY", "LUGE", "LUKE", "LULU", +"LUND", "LUNG", "LURA", "LURE", "LURK", "LUSH", "LUST", "LYLE", "LYNN", +"LYON", "LYRA", "MACE", "MADE", "MAGI", "MAID", "MAIL", "MAIN", "MAKE", +"MALE", "MALI", "MALL", "MALT", "MANA", "MANN", "MANY", "MARC", "MARE", +"MARK", "MARS", "MART", "MARY", "MASH", "MASK", "MASS", "MAST", "MATE", +"MATH", "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK", "MEET", +"MELD", "MELT", "MEMO", "MEND", "MENU", "MERT", "MESH", "MESS", "MICE", +"MIKE", "MILD", "MILE", "MILK", "MILL", "MILT", "MIMI", "MIND", "MINE", +"MINI", "MINK", "MINT", "MIRE", "MISS", "MIST", "MITE", "MITT", "MOAN", +"MOAT", "MOCK", "MODE", "MOLD", "MOLE", "MOLL", "MOLT", "MONA", "MONK", +"MONT", "MOOD", "MOON", "MOOR", "MOOT", "MORE", "MORN", "MORT", "MOSS", +"MOST", "MOTH", "MOVE", "MUCH", "MUCK", "MUDD", "MUFF", "MULE", "MULL", +"MURK", "MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL", +"NAIR", "NAME", "NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR", "NEAT", +"NECK", "NEED", "NEIL", "NELL", "NEON", "NERO", "NESS", "NEST", "NEWS", +"NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA", "NINE", "NOAH", "NODE", +"NOEL", "NOLL", "NONE", "NOOK", "NOON", "NORM", "NOSE", "NOTE", "NOUN", +"NOVA", "NUDE", "NULL", "NUMB", "OATH", "OBEY", "OBOE", "ODIN", "OHIO", +"OILY", "OINT", "OKAY", "OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", +"OMIT", "ONCE", "ONES", "ONLY", "ONTO", "ONUS", "ORAL", "ORGY", "OSLO", +"OTIS", "OTTO", "OUCH", "OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY", +"OWNS", "QUAD", "QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT", "RAGE", +"RAID", "RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE", "RASH", "RATE", +"RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", "RECK", "REED", "REEF", +"REEK", "REEL", "REID", "REIN", "RENA", "REND", "RENT", "REST", "RICE", +"RICH", "RICK", "RIDE", "RIFT", "RILL", "RIME", "RING", "RINK", "RISE", +"RISK", "RITE", "ROAD", "ROAM", "ROAR", "ROBE", "ROCK", "RODE", "ROIL", +"ROLL", "ROME", "ROOD", "ROOF", "ROOK", "ROOM", "ROOT", "ROSA", "ROSE", +"ROSS", "ROSY", "ROTH", "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", +"RUDE", "RUDY", "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", "RUSH", +"RUSK", "RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE", "SAID", "SAIL", +"SALE", "SALK", "SALT", "SAME", "SAND", "SANE", "SANG", "SANK", "SARA", +"SAUL", "SAVE", "SAYS", "SCAN", "SCAR", "SCAT", "SCOT", "SEAL", "SEAM", +"SEAR", "SEAT", "SEED", "SEEK", "SEEM", "SEEN", "SEES", "SELF", "SELL", +"SEND", "SENT", "SETS", "SEWN", "SHAG", "SHAM", "SHAW", "SHAY", "SHED", +"SHIM", "SHIN", "SHOD", "SHOE", "SHOT", "SHOW", "SHUN", "SHUT", "SICK", +"SIDE", "SIFT", "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", "SINE", +"SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", "SKID", +"SKIM", "SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY", "SLED", "SLEW", +"SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT", "SLOW", "SLUG", "SLUM", +"SLUR", "SMOG", "SMUG", "SNAG", "SNOB", "SNOW", "SNUB", "SNUG", "SOAK", +"SOAR", "SOCK", "SODA", "SOFA", "SOFT", "SOIL", "SOLD", "SOME", "SONG", +"SOON", "SOOT", "SORE", "SORT", "SOUL", "SOUR", "SOWN", "STAB", "STAG", +"STAN", "STAR", "STAY", "STEM", "STEW", "STIR", "STOW", "STUB", "STUN", +"SUCH", "SUDS", "SUIT", "SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF", +"SWAB", "SWAG", "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", "TACK", +"TACT", "TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK", "TASK", "TATE", +"TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM", "TEEN", "TEET", "TELL", +"TEND", "TENT", "TERM", "TERN", "TESS", "TEST", "THAN", "THAT", "THEE", +"THEM", "THEN", "THEY", "THIN", "THIS", "THUD", "THUG", "TICK", "TIDE", +"TIDY", "TIED", "TIER", "TILE", "TILL", "TILT", "TIME", "TINA", "TINE", +"TINT", "TINY", "TIRE", "TOAD", "TOGO", "TOIL", "TOLD", "TOLL", "TONE", +"TONG", "TONY", "TOOK", "TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR", +"TOUT", "TOWN", "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", "TRIM", +"TRIO", "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", "TUCK", "TUFT", +"TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK", "TWIG", "TWIN", "TWIT", +"ULAN", "UNIT", "URGE", "USED", "USER", "USES", "UTAH", "VAIL", "VAIN", +"VALE", "VARY", "VASE", "VAST", "VEAL", "VEDA", "VEIL", "VEIN", "VEND", +"VENT", "VERB", "VERY", "VETO", "VICE", "VIEW", "VINE", "VISE", "VOID", +"VOLT", "VOTE", "WACK", "WADE", "WAGE", "WAIL", "WAIT", "WAKE", "WALE", +"WALK", "WALL", "WALT", "WAND", "WANE", "WANG", "WANT", "WARD", "WARM", +"WARN", "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY", "WAYS", +"WEAK", "WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR", "WELD", "WELL", +"WELT", "WENT", "WERE", "WERT", "WEST", "WHAM", "WHAT", "WHEE", "WHEN", +"WHET", "WHOA", "WHOM", "WICK", "WIFE", "WILD", "WILL", "WIND", "WINE", +"WING", "WINK", "WINO", "WIRE", "WISE", "WISH", "WITH", "WOLF", "WONT", +"WOOD", "WOOL", "WORD", "WORE", "WORK", "WORM", "WORN", "WOVE", "WRIT", +"WYNN", "YALE", "YANG", "YANK", "YARD", "YARN", "YAWL", "YAWN", "YEAH", +"YEAR", "YELL", "YOGA", "YOKE" +}; + +/* + * Encode 8 bytes in 'c' as a string of English words. + * Returns a pointer to a static buffer + */ + +char *btoe(unsigned char *md) +{ + char cp[9]; /* 64 + 2 = 66 bits */ + int p, i; + static int buf[BUFSIZ]; + char *engout = (char *)buf; + + memcpy(cp, md, SKEY_SIZE); + /* compute parity */ + for(p = 0, i = 0; i < 64; i += 2) + p += extract(cp, i, 2); + cp[8] = (char)p << 6; + /* now 66 bits */ + + engout[0] = '\0'; + strncat(engout, &Wp[extract(cp, 0, 11)][0], 4); + strcat (engout," "); + strncat(engout, &Wp[extract(cp, 11, 11)][0], 4); + strcat (engout," "); + strncat(engout, &Wp[extract(cp, 22, 11)][0], 4); + strcat (engout," "); + strncat(engout, &Wp[extract(cp, 33, 11)][0], 4); + strcat (engout," "); + strncat(engout, &Wp[extract(cp, 44, 11)][0], 4); + strcat (engout," "); + strncat(engout, &Wp[extract(cp, 55, 11)][0], 4); + return (engout); + +} + + +/* + * Extract 'length' bits from the char array 's' + * starting with bit 'start' + */ + +static guint32 extract(char *s, int start, int length) +{ + guint8 cl; + guint8 cc; + guint8 cr; + guint32 x; + + /* 66 = 11 x 6 */ + + g_assert(length >= 0); + g_assert(length <= 11); + g_assert(start >= 0); + g_assert(start + length <= 66); + + cl = s[start/8]; + cc = s[start/8 + 1]; + cr = s[start/8 + 2]; + x = (guint32) ((((cl << 8) | cc) << 8) | cr); /* 24 bits */ + x = x >> (24 - (length + (start % 8))); /* cut tail */ + x = (x & (0xffff >> (16 - length))); /* cut head */ + return(x); /* length */ +} diff --git a/src/skey/btoe.h b/src/skey/btoe.h new file mode 100644 index 0000000..a2a719e --- /dev/null +++ b/src/skey/btoe.h @@ -0,0 +1,2 @@ +char *btoe(unsigned char *md); + diff --git a/src/skey/md4.c b/src/skey/md4.c new file mode 100644 index 0000000..914f101 --- /dev/null +++ b/src/skey/md4.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2001 Nikos Mavroyanopoulos + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * The algorithm is due to Ron Rivest. This code is based on code + * written by Colin Plumb in 1993. + */ + + +#include <string.h> +#include <stdlib.h> +#include "config.h" +#include "skey.h" +#include "skeyutil.h" +#include "md4.h" + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +static void byteReverse(unsigned char *buf, unsigned longs); + + +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + guint32 t; + do { + t = (guint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(guint32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +#define rotl32(x,n) (((x) << ((guint32)(n))) | ((x) >> (32 - (guint32)(n)))) + +/* + * Start MD4 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD4Init(MD4_CTX *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD4Update(MD4_CTX *ctx, unsigned char const *buf, + unsigned len) +{ + register guint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((guint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD4Transform(ctx->buf, (guint32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD4Transform(ctx->buf, (guint32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD4Final(unsigned char* digest, MD4_CTX *ctx) +{ + unsigned int count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD4Transform(ctx->buf, (guint32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((guint32 *) ctx->in)[14] = ctx->bits[0]; + ((guint32 *) ctx->in)[15] = ctx->bits[1]; + + MD4Transform(ctx->buf, (guint32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + + if (digest!=NULL) + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The three core functions */ + +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +#define FF(a, b, c, d, x, s) { \ + (a) += F ((b), (c), (d)) + (x); \ + (a) = rotl32 ((a), (s)); \ + } +#define GG(a, b, c, d, x, s) { \ + (a) += G ((b), (c), (d)) + (x) + (guint32)0x5a827999; \ + (a) = rotl32 ((a), (s)); \ + } +#define HH(a, b, c, d, x, s) { \ + (a) += H ((b), (c), (d)) + (x) + (guint32)0x6ed9eba1; \ + (a) = rotl32 ((a), (s)); \ + } + + +/* + * The core of the MD4 algorithm + */ +void MD4Transform(guint32 buf[4], guint32 const in[16]) +{ + register guint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + FF(a, b, c, d, in[0], 3); /* 1 */ + FF(d, a, b, c, in[1], 7); /* 2 */ + FF(c, d, a, b, in[2], 11); /* 3 */ + FF(b, c, d, a, in[3], 19); /* 4 */ + FF(a, b, c, d, in[4], 3); /* 5 */ + FF(d, a, b, c, in[5], 7); /* 6 */ + FF(c, d, a, b, in[6], 11); /* 7 */ + FF(b, c, d, a, in[7], 19); /* 8 */ + FF(a, b, c, d, in[8], 3); /* 9 */ + FF(d, a, b, c, in[9], 7); /* 10 */ + FF(c, d, a, b, in[10], 11); /* 11 */ + FF(b, c, d, a, in[11], 19); /* 12 */ + FF(a, b, c, d, in[12], 3); /* 13 */ + FF(d, a, b, c, in[13], 7); /* 14 */ + FF(c, d, a, b, in[14], 11); /* 15 */ + FF(b, c, d, a, in[15], 19); /* 16 */ + + GG(a, b, c, d, in[0], 3); /* 17 */ + GG(d, a, b, c, in[4], 5); /* 18 */ + GG(c, d, a, b, in[8], 9); /* 19 */ + GG(b, c, d, a, in[12], 13); /* 20 */ + GG(a, b, c, d, in[1], 3); /* 21 */ + GG(d, a, b, c, in[5], 5); /* 22 */ + GG(c, d, a, b, in[9], 9); /* 23 */ + GG(b, c, d, a, in[13], 13); /* 24 */ + GG(a, b, c, d, in[2], 3); /* 25 */ + GG(d, a, b, c, in[6], 5); /* 26 */ + GG(c, d, a, b, in[10], 9); /* 27 */ + GG(b, c, d, a, in[14], 13); /* 28 */ + GG(a, b, c, d, in[3], 3); /* 29 */ + GG(d, a, b, c, in[7], 5); /* 30 */ + GG(c, d, a, b, in[11], 9); /* 31 */ + GG(b, c, d, a, in[15], 13); /* 32 */ + + HH(a, b, c, d, in[0], 3); /* 33 */ + HH(d, a, b, c, in[8], 9); /* 34 */ + HH(c, d, a, b, in[4], 11); /* 35 */ + HH(b, c, d, a, in[12], 15); /* 36 */ + HH(a, b, c, d, in[2], 3); /* 37 */ + HH(d, a, b, c, in[10], 9); /* 38 */ + HH(c, d, a, b, in[6], 11); /* 39 */ + HH(b, c, d, a, in[14], 15); /* 40 */ + HH(a, b, c, d, in[1], 3); /* 41 */ + HH(d, a, b, c, in[9], 9); /* 42 */ + HH(c, d, a, b, in[5], 11); /* 43 */ + HH(b, c, d, a, in[13], 15); /* 44 */ + HH(a, b, c, d, in[3], 3); /* 45 */ + HH(d, a, b, c, in[11], 9); /* 46 */ + HH(c, d, a, b, in[7], 11); /* 47 */ + HH(b, c, d, a, in[15], 15); /* 48 */ + + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +int MD4Keycrunch( char *result, const char *seed, const char *passphrase) +{ + int len; + char *buf; + MD4_CTX md; + guint32 results[4]; + + len = strlen(seed) + strlen(passphrase); + buf = (char *)malloc(len+1); + if (buf == NULL) + return -1; + + strcpy(buf, seed); + skey_lowcase(buf); + strcat(buf, passphrase); + skey_sevenbit(buf); + + MD4Init(&md); + MD4Update(&md, (unsigned char *)buf, len); + MD4Final((unsigned char *)results, &md); + free(buf); + + results[0] ^= results[2]; + results[1] ^= results[3]; + memcpy((void *)result, (void *)results, SKEY_SIZE); + + return 0; +} + + +void MD4SKey(char *x) +{ + MD4_CTX md; + guint32 results[4]; + + MD4Init(&md); + MD4Update(&md, (unsigned char *)x, SKEY_SIZE); + MD4Final((unsigned char *)results, &md); + + results[0] ^= results[2]; + results[1] ^= results[3]; + + memcpy((void *)x, (void *)results, SKEY_SIZE); +} + +#ifdef MD4_MAIN + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) +{ + MD4_CTX *md4; + unsigned char digest[16]; + unsigned char data[1024]; + int i, r; + + memset(digest, 0, 16); + printf("MD4 digest algorithm. End with Ctrl-D:\n"); + + md4 = (MD4_CTX *)malloc(sizeof(MD4_CTX)); + MD4Init(md4); + do { + r = read(0, data, sizeof data); + MD4Update(md4, data, r); + } while (r); + + MD4Final(digest, md4); + printf("MD4 Digest is: "); + for (i = 0; i < 16; i++) + printf("%02X", digest[i]); + + printf("\n"); + free(md4); + return 0; +} + +#endif diff --git a/src/skey/md4.h b/src/skey/md4.h new file mode 100644 index 0000000..bd43a26 --- /dev/null +++ b/src/skey/md4.h @@ -0,0 +1,19 @@ +#ifndef MD4_H +#define MD4_H + +#include <glib.h> + +typedef struct { + guint32 buf[4]; + guint32 bits[2]; + unsigned char in[64]; +} MD4_CTX; + +void MD4Transform(guint32 buf[4], guint32 const in[16]); +void MD4Init(MD4_CTX *context); +void MD4Update(MD4_CTX *context, unsigned char const *buf, unsigned len); +void MD4Final(unsigned char *digest, MD4_CTX *context); +int MD4Keycrunch(char *result, const char *seed, const char *passphrase); +void MD4SKey(char *x); + +#endif /* !MD4_H */ diff --git a/src/skey/md5.c b/src/skey/md5.c new file mode 100644 index 0000000..9447c15 --- /dev/null +++ b/src/skey/md5.c @@ -0,0 +1,67 @@ +#include <config.h> +#include <stdlib.h> + +#include <string.h> +#include <glib.h> + +#include "skey.h" +#include "skeyutil.h" +#include "md5.h" + +int MD5Keycrunch(char *result, const char *seed, const char *passhrase) +{ + char *buf; + gsize len; + GChecksum *checksum; + guint8 digest[16]; + gsize digest_len = sizeof (digest); + guint32 *results; + + len = strlen(seed) + strlen(passhrase); + buf = (char *)g_try_malloc(len+1); + if (buf == NULL) + return -1; + + strcpy(buf, seed); + skey_lowcase(buf); + strcat(buf, passhrase); + skey_sevenbit(buf); + + checksum = g_checksum_new (G_CHECKSUM_MD5); + g_checksum_update (checksum, (const guchar *) buf, len); + g_free(buf); + + g_checksum_get_digest (checksum, digest, &digest_len); + g_assert (digest_len == 16); + + results = (guint32 *) digest; + results[0] ^= results[2]; + results[1] ^= results[3]; + + memcpy((void *)result, (void *)results, SKEY_SIZE); + + g_checksum_free (checksum); + + return 0; +} + +void MD5SKey(char *x) +{ + GChecksum *checksum; + guint8 digest[16]; + gsize digest_len = sizeof (digest); + guint32 *results; + + checksum = g_checksum_new (G_CHECKSUM_MD5); + g_checksum_update (checksum, (const guchar *) x, SKEY_SIZE); + g_checksum_get_digest (checksum, digest, &digest_len); + g_assert (digest_len == 16); + + results = (guint32 *) digest; + results[0] ^= results[2]; + results[1] ^= results[3]; + + memcpy((void *)x, (void *)results, SKEY_SIZE); + + g_checksum_free (checksum); +} diff --git a/src/skey/md5.h b/src/skey/md5.h new file mode 100644 index 0000000..3129f99 --- /dev/null +++ b/src/skey/md5.h @@ -0,0 +1,9 @@ +#ifndef MD5_H +#define MD5_H + +#include <glib.h> + +int MD5Keycrunch(char *result, const char *seed, const char *passhrase); +void MD5SKey(char *x); + +#endif /* !MD5_H */ diff --git a/src/skey/sha1.c b/src/skey/sha1.c new file mode 100644 index 0000000..ac34ccc --- /dev/null +++ b/src/skey/sha1.c @@ -0,0 +1,112 @@ +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#include <arpa/inet.h> + +#include <glib.h> + +#include "skey.h" +#include "skeyutil.h" +#include "sha1.h" + + +#define SHA1_DIGESTSIZE 20 +#define SHA1_BLOCKSIZE 64 + +#define HTONDIGEST(x) { \ + x[0] = htonl(x[0]); \ + x[1] = htonl(x[1]); \ + x[2] = htonl(x[2]); \ + x[3] = htonl(x[3]); \ + x[4] = htonl(x[4]); } + +#ifdef WORDS_BIGENDIAN +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + guint32 t; + do { + t = (guint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(guint32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + + +int SHA1Keycrunch(char *result, const char *seed, const char *passphrase) +{ + char *buf; + gsize len; + GChecksum *checksum; + guint8 digest[20]; + gsize digest_len = sizeof (digest); + guint32 *results; + + len = strlen(seed) + strlen(passphrase); + if ((buf = (char *)g_try_malloc(len+1)) == NULL) + return -1; + + strcpy(buf, seed); + skey_lowcase(buf); + strcat(buf, passphrase); + skey_sevenbit(buf); + + checksum = g_checksum_new (G_CHECKSUM_SHA1); + g_checksum_update (checksum, (const guchar *) buf, len); + g_free(buf); + + g_checksum_get_digest (checksum, digest, &digest_len); + g_assert (digest_len == 20); + + results = (guint32 *) digest; + +#ifndef WORDS_BIGENDIAN + HTONDIGEST(results); +#else + byteReverse((unsigned char *)digest, 5); +#endif + + results = (guint32 *) digest; + results[0] ^= results[2]; + results[1] ^= results[3]; + results[0] ^= results[4]; + + memcpy((void *)result, (void *)results, SKEY_SIZE); + + g_checksum_free (checksum); + + return 0; +} + +void SHA1SKey(char *x) +{ + GChecksum *checksum; + guint8 digest[20]; + gsize digest_len = sizeof (digest); + guint32 *results; + + checksum = g_checksum_new (G_CHECKSUM_SHA1); + g_checksum_update (checksum, (const guchar *) x, SKEY_SIZE); + g_checksum_get_digest (checksum, digest, &digest_len); + g_assert (digest_len == 20); + + results = (guint32 *) digest; +#ifndef WORDS_BIGENDIAN + HTONDIGEST(results); +#else + byteReverse((unsigned char *)digest, 5); +#endif + + results[0] ^= results[2]; + results[1] ^= results[3]; + results[0] ^= results[4]; + + memcpy((void *)x, (void *)results, SKEY_SIZE); + + g_checksum_free (checksum); +} diff --git a/src/skey/sha1.h b/src/skey/sha1.h new file mode 100644 index 0000000..7feed07 --- /dev/null +++ b/src/skey/sha1.h @@ -0,0 +1,9 @@ +#ifndef _SHA1_H +#define _SHA1_H + +#include <glib.h> + +int SHA1Keycrunch(char *result, const char *seed, const char *passphrase); +void SHA1SKey(char *x); + +#endif /* _SHA1_H */ diff --git a/src/skey/skey.c b/src/skey/skey.c new file mode 100644 index 0000000..2517842 --- /dev/null +++ b/src/skey/skey.c @@ -0,0 +1,37 @@ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "md4.h" +#include "md5.h" +#include "sha1.h" +#include "skey.h" +#include "btoe.h" + +struct skey_hash { + int (*Keycrunch) (char *, const char *, const char *); + void (*Skey) (char *); +}; +static struct skey_hash hash_table[] = { + { MD4Keycrunch, MD4SKey }, + { MD5Keycrunch, MD5SKey }, + { SHA1Keycrunch, SHA1SKey } +}; + + +char *skey(SKeyAlgorithm algorithm, int seq, const char *seed, const char *passphrase) +{ + char key[SKEY_SIZE]; + int i; + g_assert (algorithm < G_N_ELEMENTS (hash_table)); + if (hash_table[algorithm].Keycrunch(key, seed, passphrase) == -1) + return NULL; + + for (i = 0; i < seq; i++) + hash_table[algorithm].Skey(key); + + return strdup(btoe((unsigned char *)key)); +} diff --git a/src/skey/skey.h b/src/skey/skey.h new file mode 100644 index 0000000..0475629 --- /dev/null +++ b/src/skey/skey.h @@ -0,0 +1,10 @@ +typedef enum { + MD4, + MD5, + SHA1 +} SKeyAlgorithm; + +#define SKEY_SIZE 8 + +char *skey(SKeyAlgorithm algorithm, int seq, const char *seed, const char *passhrase); + diff --git a/src/skey/skeyutil.c b/src/skey/skeyutil.c new file mode 100644 index 0000000..b9e56dc --- /dev/null +++ b/src/skey/skeyutil.c @@ -0,0 +1,24 @@ +#include <config.h> + +#include <ctype.h> + +#include <glib.h> + +#include "skeyutil.h" + +void skey_sevenbit(char *s) +{ + char *p; + + for (p = s; *p; p++) + *p &= 0x7f; +} + +void skey_lowcase(char *s) +{ + char *p; + + for (p = s; *p; p++) + if (g_ascii_isupper(*p)) + *p = g_ascii_tolower(*p); +} diff --git a/src/skey/skeyutil.h b/src/skey/skeyutil.h new file mode 100644 index 0000000..02d4c34 --- /dev/null +++ b/src/skey/skeyutil.h @@ -0,0 +1,3 @@ +void skey_sevenbit(char *s); +void skey_lowcase(char *s); + diff --git a/src/skey/test.c b/src/skey/test.c new file mode 100644 index 0000000..1fc1a09 --- /dev/null +++ b/src/skey/test.c @@ -0,0 +1,99 @@ +#include <config.h> + +#include <locale.h> +#include <stdlib.h> +#include <string.h> + +#include <glib.h> + +#include "skey.h" + +typedef struct { + SKeyAlgorithm algorithm; + const char *passphrase; + const char *seed; + int count; + const char *hex; + const char *btoe; +} TestEntry; + +static const TestEntry tests[] = { + { MD4, "This is a test.", "TeSt", 0, "D185 4218 EBBB 0B51", "ROME MUG FRED SCAN LIVE LACE" }, + { MD4, "This is a test.", "TeSt", 1, "6347 3EF0 1CD0 B444", "CARD SAD MINI RYE COL KIN" }, + { MD4, "This is a test.", "TeSt", 99, "C5E6 1277 6E6C 237A", "NOTE OUT IBIS SINK NAVE MODE" }, + { MD4, "AbCdEfGhIjK", "alpha1", 0, "5007 6F47 EB1A DE4E", "AWAY SEN ROOK SALT LICE MAP" }, + { MD4, "AbCdEfGhIjK", "alpha1", 1, "65D2 0D19 49B5 F7AB", "CHEW GRIM WU HANG BUCK SAID" }, + { MD4, "AbCdEfGhIjK", "alpha1", 99, "D150 C82C CE6F 62D1", "ROIL FREE COG HUNK WAIT COCA" }, + { MD4, "OTP's are good", "correct", 0, "849C 79D4 F6F5 5388", "FOOL STEM DONE TOOL BECK NILE" }, + { MD4, "OTP's are good", "correct", 1, "8C09 92FB 2508 47B1", "GIST AMOS MOOT AIDS FOOD SEEM" }, + { MD4, "OTP's are good", "correct", 99, "3F3B F4B4 145F D74B", "TAG SLOW NOV MIN WOOL KENO" }, + { MD5, "This is a test.", "TeSt", 0, "9E87 6134 D904 99DD", "INCH SEA ANNE LONG AHEM TOUR" }, + { MD5, "This is a test.", "TeSt", 1, "7965 E054 36F5 029F", "EASE OIL FUM CURE AWRY AVIS" }, + { MD5, "This is a test.", "TeSt", 99, "50FE 1962 C496 5880", "BAIL TUFT BITS GANG CHEF THY" }, + { MD5, "AbCdEfGhIjK", "alpha1", 0, "8706 6DD9 644B F206", "FULL PEW DOWN ONCE MORT ARC" }, + { MD5, "AbCdEfGhIjK", "alpha1", 1, "7CD3 4C10 40AD D14B", "FACT HOOF AT FIST SITE KENT" }, + { MD5, "AbCdEfGhIjK", "alpha1", 99, "5AA3 7A81 F212 146C", "BODE HOP JAKE STOW JUT RAP" }, + { MD5, "OTP's are good", "correct", 0, "F205 7539 43DE 4CF9", "ULAN NEW ARMY FUSE SUIT EYED" }, + { MD5, "OTP's are good", "correct", 1, "DDCD AC95 6F23 4937", "SKIM CULT LOB SLAM POE HOWL" }, + { MD5, "OTP's are good", "correct", 99, "B203 E28F A525 BE47", "LONG IVY JULY AJAR BOND LEE" }, + { SHA1, "This is a test.", "TeSt", 0, "BB9E 6AE1 979D 8FF4", "MILT VARY MAST OK SEES WENT" }, + { SHA1, "This is a test.", "TeSt", 1, "63D9 3663 9734 385B", "CART OTTO HIVE ODE VAT NUT" }, + { SHA1, "This is a test.", "TeSt", 99, "87FE C776 8B73 CCF9", "GAFF WAIT SKID GIG SKY EYED" }, + { SHA1, "AbCdEfGhIjK", "alpha1", 0, "AD85 F658 EBE3 83C9", "LEST OR HEEL SCOT ROB SUIT" }, + { SHA1, "AbCdEfGhIjK", "alpha1", 1, "D07C E229 B5CF 119B", "RITE TAKE GELD COST TUNE RECK" }, + { SHA1, "AbCdEfGhIjK", "alpha1", 99, "27BC 7103 5AAF 3DC6", "MAY STAR TIN LYON VEDA STAN" }, + { SHA1, "OTP's are good", "correct", 0, "D51F 3E99 BF8E 6F0B", "RUST WELT KICK FELL TAIL FRAU" }, + { SHA1, "OTP's are good", "correct", 1, "82AE B52D 9437 74E4", "FLIT DOSE ALSO MEW DRUM DEFY" }, + { SHA1, "OTP's are good", "correct", 99, "4F29 6A74 FE15 67EC", "AURA ALOE HURL WING BERG WAIT" }, + + { SHA1, "Passphrase", "IiIi", 100, "27F4 01CC 0AC8 5112", "MEG JACK DIET GAD FORK GARY" } +}; + +static const char *algos[] = { + "MD4", + "MD5", + "SHA1" +}; + +static void +skey_test (gconstpointer data) +{ + const TestEntry *test = (const TestEntry *) data; + char *key; + + key = skey (test->algorithm, + test->count, + test->seed, + test->passphrase); + g_assert (key != NULL); + g_assert (strcmp (key, test->btoe) == 0); + free (key); +} + +int main(int argc, char *argv[]) +{ + guint i; + + if (!setlocale (LC_ALL, "")) + g_error ("Locale not supported by C library!\n"); + + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugzilla.mate.org/enter_bug.cgi?product=mate-terminal"); + + for (i = 0; i < G_N_ELEMENTS (tests); ++i) { + const TestEntry *test = &tests[i]; + char *name; + + name = g_strdup_printf ("/%s/%s/%s/%d/%s/%s", + algos[test->algorithm], + test->passphrase, + test->seed, + test->count, + test->hex, + test->btoe); + g_test_add_data_func (name, test, skey_test); + g_free (name); + } + + return g_test_run (); +} diff --git a/src/terminal-accels.c b/src/terminal-accels.c new file mode 100644 index 0000000..73ed7ca --- /dev/null +++ b/src/terminal-accels.c @@ -0,0 +1,1012 @@ +/* + * Copyright © 2001, 2002 Havoc Pennington, Red Hat Inc. + * Copyright © 2008 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> + +#include <gdk/gdkkeysyms.h> + +#include "terminal-accels.h" +#include "terminal-app.h" +#include "terminal-debug.h" +#include "terminal-intl.h" +#include "terminal-profile.h" +#include "terminal-util.h" + +/* NOTES + * + * There are two sources of keybindings changes, from MateConf and from + * the accel map (happens with in-place menu editing). + * + * When a keybinding mateconf key changes, we propagate that into the + * accel map. + * When the accel map changes, we queue a sync to mateconf. + * + * To avoid infinite loops, we short-circuit in both directions + * if the value is unchanged from last known. + * + * In the keybinding editor, when editing or clearing an accel, we write + * the change directly to mateconf and rely on the mateconf callback to + * actually apply the change to the accel map. + */ + +#define ACCEL_PATH_ROOT "<Actions>/Main/" +#define ACCEL_PATH_NEW_TAB ACCEL_PATH_ROOT "FileNewTab" +#define ACCEL_PATH_NEW_WINDOW ACCEL_PATH_ROOT "FileNewWindow" +#define ACCEL_PATH_NEW_PROFILE ACCEL_PATH_ROOT "FileNewProfile" +#define ACCEL_PATH_SAVE_CONTENTS ACCEL_PATH_ROOT "FileSaveContents" +#define ACCEL_PATH_CLOSE_TAB ACCEL_PATH_ROOT "FileCloseTab" +#define ACCEL_PATH_CLOSE_WINDOW ACCEL_PATH_ROOT "FileCloseWindow" +#define ACCEL_PATH_COPY ACCEL_PATH_ROOT "EditCopy" +#define ACCEL_PATH_PASTE ACCEL_PATH_ROOT "EditPaste" +#define ACCEL_PATH_TOGGLE_MENUBAR ACCEL_PATH_ROOT "ViewMenubar" +#define ACCEL_PATH_FULL_SCREEN ACCEL_PATH_ROOT "ViewFullscreen" +#define ACCEL_PATH_RESET ACCEL_PATH_ROOT "TerminalReset" +#define ACCEL_PATH_RESET_AND_CLEAR ACCEL_PATH_ROOT "TerminalResetClear" +#define ACCEL_PATH_PREV_TAB ACCEL_PATH_ROOT "TabsPrevious" +#define ACCEL_PATH_NEXT_TAB ACCEL_PATH_ROOT "TabsNext" +#define ACCEL_PATH_SET_TERMINAL_TITLE ACCEL_PATH_ROOT "TerminalSetTitle" +#define ACCEL_PATH_HELP ACCEL_PATH_ROOT "HelpContents" +#define ACCEL_PATH_ZOOM_IN ACCEL_PATH_ROOT "ViewZoomIn" +#define ACCEL_PATH_ZOOM_OUT ACCEL_PATH_ROOT "ViewZoomOut" +#define ACCEL_PATH_ZOOM_NORMAL ACCEL_PATH_ROOT "ViewZoom100" +#define ACCEL_PATH_MOVE_TAB_LEFT ACCEL_PATH_ROOT "TabsMoveLeft" +#define ACCEL_PATH_MOVE_TAB_RIGHT ACCEL_PATH_ROOT "TabsMoveRight" +#define ACCEL_PATH_DETACH_TAB ACCEL_PATH_ROOT "TabsDetach" +#define ACCEL_PATH_SWITCH_TAB_PREFIX ACCEL_PATH_ROOT "TabsSwitch" + +#define KEY_CLOSE_TAB CONF_KEYS_PREFIX "/close_tab" +#define KEY_CLOSE_WINDOW CONF_KEYS_PREFIX "/close_window" +#define KEY_COPY CONF_KEYS_PREFIX "/copy" +#define KEY_DETACH_TAB CONF_KEYS_PREFIX "/detach_tab" +#define KEY_FULL_SCREEN CONF_KEYS_PREFIX "/full_screen" +#define KEY_HELP CONF_KEYS_PREFIX "/help" +#define KEY_MOVE_TAB_LEFT CONF_KEYS_PREFIX "/move_tab_left" +#define KEY_MOVE_TAB_RIGHT CONF_KEYS_PREFIX "/move_tab_right" +#define KEY_NEW_PROFILE CONF_KEYS_PREFIX "/new_profile" +#define KEY_NEW_TAB CONF_KEYS_PREFIX "/new_tab" +#define KEY_NEW_WINDOW CONF_KEYS_PREFIX "/new_window" +#define KEY_NEXT_TAB CONF_KEYS_PREFIX "/next_tab" +#define KEY_PASTE CONF_KEYS_PREFIX "/paste" +#define KEY_PREV_TAB CONF_KEYS_PREFIX "/prev_tab" +#define KEY_RESET_AND_CLEAR CONF_KEYS_PREFIX "/reset_and_clear" +#define KEY_RESET CONF_KEYS_PREFIX "/reset" +#define KEY_SAVE_CONTENTS CONF_KEYS_PREFIX "/save_contents" +#define KEY_SET_TERMINAL_TITLE CONF_KEYS_PREFIX "/set_window_title" +#define KEY_TOGGLE_MENUBAR CONF_KEYS_PREFIX "/toggle_menubar" +#define KEY_ZOOM_IN CONF_KEYS_PREFIX "/zoom_in" +#define KEY_ZOOM_NORMAL CONF_KEYS_PREFIX "/zoom_normal" +#define KEY_ZOOM_OUT CONF_KEYS_PREFIX "/zoom_out" +#define KEY_SWITCH_TAB_PREFIX CONF_KEYS_PREFIX "/switch_to_tab_" + +#if 1 +/* +* We don't want to enable content saving until vte supports it async. +* So we disable this code for stable versions. +*/ +#include "terminal-version.h" + +#if (TERMINAL_MINOR_VERSION & 1) != 0 +#define ENABLE_SAVE +#else +#undef ENABLE_SAVE +#endif +#endif + +typedef struct +{ + const char *user_visible_name; + const char *mateconf_key; + const char *accel_path; + /* last values received from mateconf */ + GdkModifierType mateconf_mask; + guint mateconf_keyval; + GClosure *closure; + /* have gotten a notification from gtk */ + gboolean needs_mateconf_sync; + gboolean accel_path_unlocked; +} KeyEntry; + +typedef struct +{ + KeyEntry *key_entry; + guint n_elements; + const char *user_visible_name; +} KeyEntryList; + +static KeyEntry file_entries[] = +{ + { N_("New Tab"), + KEY_NEW_TAB, ACCEL_PATH_NEW_TAB, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_t, NULL, FALSE, TRUE }, + { N_("New Window"), + KEY_NEW_WINDOW, ACCEL_PATH_NEW_WINDOW, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_n, NULL, FALSE, TRUE }, + { N_("New Profile"), + KEY_NEW_PROFILE, ACCEL_PATH_NEW_PROFILE, 0, 0, NULL, FALSE, TRUE }, +#ifdef ENABLE_SAVE + { N_("Save Contents"), + KEY_SAVE_CONTENTS, ACCEL_PATH_SAVE_CONTENTS, 0, 0, NULL, FALSE, TRUE }, +#endif + { N_("Close Tab"), + KEY_CLOSE_TAB, ACCEL_PATH_CLOSE_TAB, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_w, NULL, FALSE, TRUE }, + { N_("Close Window"), + KEY_CLOSE_WINDOW, ACCEL_PATH_CLOSE_WINDOW, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_q, NULL, FALSE, TRUE }, +}; + +static KeyEntry edit_entries[] = +{ + { N_("Copy"), + KEY_COPY, ACCEL_PATH_COPY, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_c, NULL, FALSE, TRUE }, + { N_("Paste"), + KEY_PASTE, ACCEL_PATH_PASTE, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_v, NULL, FALSE, TRUE }, +}; + +static KeyEntry view_entries[] = +{ + { N_("Hide and Show menubar"), + KEY_TOGGLE_MENUBAR, ACCEL_PATH_TOGGLE_MENUBAR, 0, 0, NULL, FALSE, TRUE }, + { N_("Full Screen"), + KEY_FULL_SCREEN, ACCEL_PATH_FULL_SCREEN, 0, GDK_F11, NULL, FALSE, TRUE }, + { N_("Zoom In"), + KEY_ZOOM_IN, ACCEL_PATH_ZOOM_IN, GDK_CONTROL_MASK, GDK_plus, NULL, FALSE, TRUE }, + { N_("Zoom Out"), + KEY_ZOOM_OUT, ACCEL_PATH_ZOOM_OUT, GDK_CONTROL_MASK, GDK_minus, NULL, FALSE, TRUE }, + { N_("Normal Size"), + KEY_ZOOM_NORMAL, ACCEL_PATH_ZOOM_NORMAL, GDK_CONTROL_MASK, GDK_0, NULL, FALSE, TRUE } +}; + +static KeyEntry terminal_entries[] = +{ + { N_("Set Title"), + KEY_SET_TERMINAL_TITLE, ACCEL_PATH_SET_TERMINAL_TITLE, 0, 0, NULL, FALSE, TRUE }, + { N_("Reset"), + KEY_RESET, ACCEL_PATH_RESET, 0, 0, NULL, FALSE, TRUE }, + { N_("Reset and Clear"), + KEY_RESET_AND_CLEAR, ACCEL_PATH_RESET_AND_CLEAR, 0, 0, NULL, FALSE, TRUE }, +}; + +static KeyEntry tabs_entries[] = +{ + { N_("Switch to Previous Tab"), + KEY_PREV_TAB, ACCEL_PATH_PREV_TAB, GDK_CONTROL_MASK, GDK_Page_Up, NULL, FALSE, TRUE }, + { N_("Switch to Next Tab"), + KEY_NEXT_TAB, ACCEL_PATH_NEXT_TAB, GDK_CONTROL_MASK, GDK_Page_Down, NULL, FALSE, TRUE }, + { N_("Move Tab to the Left"), + KEY_MOVE_TAB_LEFT, ACCEL_PATH_MOVE_TAB_LEFT, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_Page_Up, NULL, FALSE, TRUE }, + { N_("Move Tab to the Right"), + KEY_MOVE_TAB_RIGHT, ACCEL_PATH_MOVE_TAB_RIGHT, GDK_SHIFT_MASK | GDK_CONTROL_MASK, GDK_Page_Down, NULL, FALSE, TRUE }, + { N_("Detach Tab"), + KEY_DETACH_TAB, ACCEL_PATH_DETACH_TAB, 0, 0, NULL, FALSE, TRUE }, + { N_("Switch to Tab 1"), + KEY_SWITCH_TAB_PREFIX "1", + ACCEL_PATH_SWITCH_TAB_PREFIX "1", GDK_MOD1_MASK, GDK_1, NULL, FALSE, TRUE }, + { N_("Switch to Tab 2"), + KEY_SWITCH_TAB_PREFIX "2", + ACCEL_PATH_SWITCH_TAB_PREFIX "2", GDK_MOD1_MASK, GDK_2, NULL, FALSE, TRUE }, + { N_("Switch to Tab 3"), + KEY_SWITCH_TAB_PREFIX "3", + ACCEL_PATH_SWITCH_TAB_PREFIX "3", GDK_MOD1_MASK, GDK_3, NULL, FALSE, TRUE }, + { N_("Switch to Tab 4"), + KEY_SWITCH_TAB_PREFIX "4", + ACCEL_PATH_SWITCH_TAB_PREFIX "4", GDK_MOD1_MASK, GDK_4, NULL, FALSE, TRUE }, + { N_("Switch to Tab 5"), + KEY_SWITCH_TAB_PREFIX "5", + ACCEL_PATH_SWITCH_TAB_PREFIX "5", GDK_MOD1_MASK, GDK_5, NULL, FALSE, TRUE }, + { N_("Switch to Tab 6"), + KEY_SWITCH_TAB_PREFIX "6", + ACCEL_PATH_SWITCH_TAB_PREFIX "6", GDK_MOD1_MASK, GDK_6, NULL, FALSE, TRUE }, + { N_("Switch to Tab 7"), + KEY_SWITCH_TAB_PREFIX "7", + ACCEL_PATH_SWITCH_TAB_PREFIX "7", GDK_MOD1_MASK, GDK_7, NULL, FALSE, TRUE }, + { N_("Switch to Tab 8"), + KEY_SWITCH_TAB_PREFIX "8", + ACCEL_PATH_SWITCH_TAB_PREFIX "8", GDK_MOD1_MASK, GDK_8, NULL, FALSE, TRUE }, + { N_("Switch to Tab 9"), + KEY_SWITCH_TAB_PREFIX "9", + ACCEL_PATH_SWITCH_TAB_PREFIX "9", GDK_MOD1_MASK, GDK_9, NULL, FALSE, TRUE }, + { N_("Switch to Tab 10"), + KEY_SWITCH_TAB_PREFIX "10", + ACCEL_PATH_SWITCH_TAB_PREFIX "10", GDK_MOD1_MASK, GDK_0, NULL, FALSE, TRUE }, + { N_("Switch to Tab 11"), + KEY_SWITCH_TAB_PREFIX "11", + ACCEL_PATH_SWITCH_TAB_PREFIX "11", 0, 0, NULL, FALSE, TRUE }, + { N_("Switch to Tab 12"), + KEY_SWITCH_TAB_PREFIX "12", + ACCEL_PATH_SWITCH_TAB_PREFIX "12", 0, 0, NULL, FALSE, TRUE } +}; + +static KeyEntry help_entries[] = { + { N_("Contents"), KEY_HELP, ACCEL_PATH_HELP, 0, GDK_F1, NULL, FALSE, TRUE } +}; + +static KeyEntryList all_entries[] = +{ + { file_entries, G_N_ELEMENTS (file_entries), N_("File") }, + { edit_entries, G_N_ELEMENTS (edit_entries), N_("Edit") }, + { view_entries, G_N_ELEMENTS (view_entries), N_("View") }, + { terminal_entries, G_N_ELEMENTS (terminal_entries), N_("Terminal") }, + { tabs_entries, G_N_ELEMENTS (tabs_entries), N_("Tabs") }, + { help_entries, G_N_ELEMENTS (help_entries), N_("Help") } +}; + +enum +{ + ACTION_COLUMN, + KEYVAL_COLUMN, + N_COLUMNS +}; + +static void keys_change_notify (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data); + +static void accel_changed_callback (GtkAccelGroup *accel_group, + guint keyval, + GdkModifierType modifier, + GClosure *accel_closure, + gpointer data); + +static gboolean binding_from_string (const char *str, + guint *accelerator_key, + GdkModifierType *accelerator_mods); + +static gboolean binding_from_value (MateConfValue *value, + guint *accelerator_key, + GdkModifierType *accelerator_mods); + +static gboolean sync_idle_cb (gpointer data); + +static guint sync_idle_id = 0; +static GtkAccelGroup *notification_group = NULL; +/* never set mateconf keys in response to receiving a mateconf notify. */ +static int inside_mateconf_notify = 0; +static GtkWidget *edit_keys_dialog = NULL; +static GtkTreeStore *edit_keys_store = NULL; +static guint mateconf_notify_id; +static GHashTable *mateconf_key_to_entry; + +static char* +binding_name (guint keyval, + GdkModifierType mask) +{ + if (keyval != 0) + return gtk_accelerator_name (keyval, mask); + + return g_strdup ("disabled"); +} + +static char* +binding_display_name (guint keyval, + GdkModifierType mask) +{ + if (keyval != 0) + return gtk_accelerator_get_label (keyval, mask); + + return g_strdup (_("Disabled")); +} + +static const char * +key_from_mateconf_key (const char *mateconf_key) +{ + const char *last_slash = strrchr (mateconf_key, '/'); + if (last_slash) + return ++last_slash; + return NULL; +} + +void +terminal_accels_init (void) +{ + MateConfClient *conf; + guint i, j; + + conf = mateconf_client_get_default (); + + mateconf_client_add_dir (conf, CONF_KEYS_PREFIX, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + mateconf_notify_id = + mateconf_client_notify_add (conf, + CONF_KEYS_PREFIX, + keys_change_notify, + NULL, NULL, NULL); + + mateconf_key_to_entry = g_hash_table_new (g_str_hash, g_str_equal); + + notification_group = gtk_accel_group_new (); + + for (i = 0; i < G_N_ELEMENTS (all_entries); ++i) + { + for (j = 0; j < all_entries[i].n_elements; ++j) + { + KeyEntry *key_entry; + + key_entry = &(all_entries[i].key_entry[j]); + + g_hash_table_insert (mateconf_key_to_entry, + (gpointer) key_from_mateconf_key (key_entry->mateconf_key), + key_entry); + + key_entry->closure = g_closure_new_simple (sizeof (GClosure), key_entry); + + g_closure_ref (key_entry->closure); + g_closure_sink (key_entry->closure); + + gtk_accel_group_connect_by_path (notification_group, + I_(key_entry->accel_path), + key_entry->closure); + + mateconf_client_notify (conf, key_entry->mateconf_key); + } + } + + g_object_unref (conf); + + g_signal_connect (notification_group, "accel-changed", + G_CALLBACK (accel_changed_callback), NULL); +} + +void +terminal_accels_shutdown (void) +{ + MateConfClient *conf; + + conf = mateconf_client_get_default (); + mateconf_client_notify_remove (conf, mateconf_notify_id); + mateconf_client_remove_dir (conf, CONF_KEYS_PREFIX, NULL); + g_object_unref (conf); + + if (sync_idle_id != 0) + { + g_source_remove (sync_idle_id); + sync_idle_id = 0; + + sync_idle_cb (NULL); + } + + g_hash_table_destroy (mateconf_key_to_entry); + mateconf_key_to_entry = NULL; + + g_object_unref (notification_group); + notification_group = NULL; +} + +static gboolean +update_model_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + KeyEntry *key_entry = NULL; + + gtk_tree_model_get (model, iter, + KEYVAL_COLUMN, &key_entry, + -1); + + if (key_entry == (KeyEntry *) data) + { + gtk_tree_model_row_changed (model, path, iter); + return TRUE; + } + return FALSE; +} + +static void +keys_change_notify (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + MateConfValue *val; + KeyEntry *key_entry; + GdkModifierType mask; + guint keyval; + + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + "key %s changed\n", + mateconf_entry_get_key (entry)); + + val = mateconf_entry_get_value (entry); + +#ifdef MATE_ENABLE_DEBUG + _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_ACCELS) + { + if (val == NULL) + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, " changed to be unset\n"); + else if (val->type != MATECONF_VALUE_STRING) + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, " changed to non-string value\n"); + else + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + " changed to \"%s\"\n", + mateconf_value_get_string (val)); + } +#endif + + key_entry = g_hash_table_lookup (mateconf_key_to_entry, key_from_mateconf_key (mateconf_entry_get_key (entry))); + if (!key_entry) + { + /* shouldn't really happen, but let's be safe */ + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + " WARNING: KeyEntry for changed key not found, bailing out\n"); + return; + } + + if (!binding_from_value (val, &keyval, &mask)) + { + const char *str = val->type == MATECONF_VALUE_STRING ? mateconf_value_get_string (val) : NULL; + g_printerr ("The value \"%s\" of configuration key %s is not a valid accelerator\n", + str ? str : "(null)", + key_entry->mateconf_key); + return; + } + + key_entry->mateconf_keyval = keyval; + key_entry->mateconf_mask = mask; + + /* Unlock the path, so we can change its accel */ + if (!key_entry->accel_path_unlocked) + gtk_accel_map_unlock_path (key_entry->accel_path); + + /* sync over to GTK */ + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + "changing path %s to %s\n", + key_entry->accel_path, + binding_name (keyval, mask)); /* memleak */ + inside_mateconf_notify += 1; + /* Note that this may return FALSE, e.g. when the entry was already set correctly. */ + gtk_accel_map_change_entry (key_entry->accel_path, + keyval, mask, + TRUE); + inside_mateconf_notify -= 1; + + /* Lock the path if the mateconf key isn't writable */ + key_entry->accel_path_unlocked = mateconf_entry_get_is_writable (entry); + if (!key_entry->accel_path_unlocked) + gtk_accel_map_lock_path (key_entry->accel_path); + + /* This seems necessary to update the tree model, since sometimes the + * notification on the notification_group seems not to be emitted correctly. + * Without this change, when trying to set an accel to e.g. Alt-T (while the main + * menu in the terminal windows is _Terminal with Alt-T mnemonic) only displays + * the accel change after a re-expose of the row. + * FIXME: Find out *why* the accel-changed signal is wrong here! + */ + if (edit_keys_store) + gtk_tree_model_foreach (GTK_TREE_MODEL (edit_keys_store), update_model_foreach, key_entry); +} + +static void +accel_changed_callback (GtkAccelGroup *accel_group, + guint keyval, + GdkModifierType modifier, + GClosure *accel_closure, + gpointer data) +{ + /* FIXME because GTK accel API is so nonsensical, we get + * a notify for each closure, on both the added and the removed + * accelerator. We just use the accel closure to find our + * accel entry, then update the value of that entry. + * We use an idle function to avoid setting the entry + * in mateconf when the accelerator gets removed and then + * setting it again when it gets added. + */ + KeyEntry *key_entry; + + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + "Changed accel %s closure %p\n", + binding_name (keyval, modifier), /* memleak */ + accel_closure); + + if (inside_mateconf_notify) + { + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + "Ignoring change from gtk because we're inside a mateconf notify\n"); + return; + } + + key_entry = accel_closure->data; + g_assert (key_entry); + + key_entry->needs_mateconf_sync = TRUE; + + if (sync_idle_id == 0) + sync_idle_id = g_idle_add (sync_idle_cb, NULL); +} + +static gboolean +binding_from_string (const char *str, + guint *accelerator_key, + GdkModifierType *accelerator_mods) +{ + if (str == NULL || + strcmp (str, "disabled") == 0) + { + *accelerator_key = 0; + *accelerator_mods = 0; + return TRUE; + } + + gtk_accelerator_parse (str, accelerator_key, accelerator_mods); + if (*accelerator_key == 0 && + *accelerator_mods == 0) + return FALSE; + + return TRUE; +} + +static gboolean +binding_from_value (MateConfValue *value, + guint *accelerator_key, + GdkModifierType *accelerator_mods) +{ + if (value == NULL) + { + /* unset */ + *accelerator_key = 0; + *accelerator_mods = 0; + return TRUE; + } + + if (value->type != MATECONF_VALUE_STRING) + return FALSE; + + return binding_from_string (mateconf_value_get_string (value), + accelerator_key, + accelerator_mods); +} + +static void +add_key_entry_to_changeset (gpointer key, + KeyEntry *key_entry, + MateConfChangeSet *changeset) +{ + GtkAccelKey gtk_key; + + if (!key_entry->needs_mateconf_sync) + return; + + key_entry->needs_mateconf_sync = FALSE; + + if (gtk_accel_map_lookup_entry (key_entry->accel_path, >k_key) && + (gtk_key.accel_key != key_entry->mateconf_keyval || + gtk_key.accel_mods != key_entry->mateconf_mask)) + { + char *accel_name; + + accel_name = binding_name (gtk_key.accel_key, gtk_key.accel_mods); + mateconf_change_set_set_string (changeset, key_entry->mateconf_key, accel_name); + g_free (accel_name); + } +} + +static gboolean +sync_idle_cb (gpointer data) +{ + MateConfClient *conf; + MateConfChangeSet *changeset; + GError *error = NULL; + + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + "mateconf sync handler\n"); + + sync_idle_id = 0; + + conf = mateconf_client_get_default (); + + changeset = mateconf_change_set_new (); + g_hash_table_foreach (mateconf_key_to_entry, (GHFunc) add_key_entry_to_changeset, changeset); + if (!mateconf_client_commit_change_set (conf, changeset, TRUE, &error)) + { + g_printerr ("Error committing the accelerator changeset: %s\n", error->message); + g_error_free (error); + } + + mateconf_change_set_unref (changeset); + g_object_unref (conf); + + return FALSE; +} + +/* We have the same KeyEntry* in both columns; + * we only have two columns because we want to be able + * to sort by either one of them. + */ + +static void +accel_set_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + KeyEntry *ke; + + gtk_tree_model_get (model, iter, + KEYVAL_COLUMN, &ke, + -1); + + if (ke == NULL) + /* This is a title row */ + g_object_set (cell, + "visible", FALSE, + NULL); + else + g_object_set (cell, + "visible", TRUE, + "sensitive", ke->accel_path_unlocked, + "editable", ke->accel_path_unlocked, + "accel-key", ke->mateconf_keyval, + "accel-mods", ke->mateconf_mask, + NULL); +} + +static int +accel_compare_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + KeyEntry *ke_a; + KeyEntry *ke_b; + char *name_a; + char *name_b; + int result; + + gtk_tree_model_get (model, a, + KEYVAL_COLUMN, &ke_a, + -1); + if (ke_a == NULL) + { + gtk_tree_model_get (model, a, + ACTION_COLUMN, &name_a, + -1); + } + else + { + name_a = binding_display_name (ke_a->mateconf_keyval, + ke_a->mateconf_mask); + } + + gtk_tree_model_get (model, b, + KEYVAL_COLUMN, &ke_b, + -1); + if (ke_b == NULL) + { + gtk_tree_model_get (model, b, + ACTION_COLUMN, &name_b, + -1); + } + else + { + name_b = binding_display_name (ke_b->mateconf_keyval, + ke_b->mateconf_mask); + } + + result = g_utf8_collate (name_a, name_b); + + g_free (name_a); + g_free (name_b); + + return result; +} + +static void +treeview_accel_changed_cb (GtkAccelGroup *accel_group, + guint keyval, + GdkModifierType modifier, + GClosure *accel_closure, + GtkTreeModel *model) +{ + gtk_tree_model_foreach (model, update_model_foreach, accel_closure->data); +} + +static void +accel_edited_callback (GtkCellRendererAccel *cell, + gchar *path_string, + guint keyval, + GdkModifierType mask, + guint hardware_keycode, + GtkTreeView *view) +{ + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + KeyEntry *ke; + GtkAccelGroupEntry *entries; + guint n_entries; + char *str; + MateConfClient *conf; + + model = gtk_tree_view_get_model (view); + + path = gtk_tree_path_new_from_string (path_string); + if (!path) + return; + + if (!gtk_tree_model_get_iter (model, &iter, path)) { + gtk_tree_path_free (path); + return; + } + gtk_tree_path_free (path); + + gtk_tree_model_get (model, &iter, KEYVAL_COLUMN, &ke, -1); + + /* sanity check */ + if (ke == NULL) + return; + + /* Check if we already have an entry using this accel */ + entries = gtk_accel_group_query (notification_group, keyval, mask, &n_entries); + if (n_entries > 0) + { + if (entries[0].accel_path_quark != g_quark_from_string (ke->accel_path)) + { + GtkWidget *dialog; + char *name; + KeyEntry *other_key; + + name = gtk_accelerator_get_label (keyval, mask); + other_key = entries[0].closure->data; + g_assert (other_key); + + dialog = + gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK, + _("The shortcut key “%s” is already bound to the “%s” action"), + name, + other_key->user_visible_name ? _(other_key->user_visible_name) : other_key->mateconf_key); + g_free (name); + + g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); + gtk_window_present (GTK_WINDOW (dialog)); + } + + return; + } + + str = binding_name (keyval, mask); + + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + "Edited path %s keyval %s, setting mateconf to %s\n", + ke->accel_path, + gdk_keyval_name (keyval) ? gdk_keyval_name (keyval) : "null", + str); +#ifdef MATE_ENABLE_DEBUG + _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_ACCELS) + { + GtkAccelKey old_key; + + if (gtk_accel_map_lookup_entry (ke->accel_path, &old_key)) { + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + " Old entry of path %s is keyval %s mask %x\n", + ke->accel_path, gdk_keyval_name (old_key.accel_key), old_key.accel_mods); + } else { + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + " Failed to look up the old entry of path %s\n", + ke->accel_path); + } + } +#endif + + conf = mateconf_client_get_default (); + mateconf_client_set_string (conf, + ke->mateconf_key, + str, + NULL); + g_object_unref (conf); + g_free (str); +} + +static void +accel_cleared_callback (GtkCellRendererAccel *cell, + gchar *path_string, + GtkTreeView *view) +{ + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + KeyEntry *ke; + char *str; + MateConfClient *conf; + + model = gtk_tree_view_get_model (view); + + path = gtk_tree_path_new_from_string (path_string); + if (!path) + return; + + if (!gtk_tree_model_get_iter (model, &iter, path)) { + gtk_tree_path_free (path); + return; + } + gtk_tree_path_free (path); + + gtk_tree_model_get (model, &iter, KEYVAL_COLUMN, &ke, -1); + + /* sanity check */ + if (ke == NULL) + return; + + ke->mateconf_keyval = 0; + ke->mateconf_mask = 0; + ke->needs_mateconf_sync = TRUE; + + str = binding_name (0, 0); + + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + "Cleared keybinding for mateconf %s", + ke->mateconf_key); + + conf = mateconf_client_get_default (); + mateconf_client_set_string (conf, + ke->mateconf_key, + str, + NULL); + g_object_unref (conf); + g_free (str); +} + +static void +edit_keys_dialog_destroy_cb (GtkWidget *widget, + gpointer user_data) +{ + g_signal_handlers_disconnect_by_func (notification_group, G_CALLBACK (treeview_accel_changed_cb), user_data); + edit_keys_dialog = NULL; + edit_keys_store = NULL; +} + +static void +edit_keys_dialog_response_cb (GtkWidget *editor, + int response, + gpointer use_data) +{ + if (response == GTK_RESPONSE_HELP) + { + terminal_util_show_help ("mate-terminal-shortcuts", GTK_WINDOW (editor)); + return; + } + + gtk_widget_destroy (editor); +} + +#ifdef MATE_ENABLE_DEBUG +static void +row_changed (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + _terminal_debug_print (TERMINAL_DEBUG_ACCELS, + "ROW-CHANGED [%s]\n", gtk_tree_path_to_string (path) /* leak */); +} +#endif + +void +terminal_edit_keys_dialog_show (GtkWindow *transient_parent) +{ + TerminalApp *app; + GtkWidget *dialog, *tree_view, *disable_mnemonics_button, *disable_menu_accel_button; + GtkTreeViewColumn *column; + GtkCellRenderer *cell_renderer; + GtkTreeStore *tree; + guint i; + + if (edit_keys_dialog != NULL) + goto done; + + if (!terminal_util_load_builder_file ("keybinding-editor.ui", + "keybindings-dialog", &dialog, + "disable-mnemonics-checkbutton", &disable_mnemonics_button, + "disable-menu-accel-checkbutton", &disable_menu_accel_button, + "accelerators-treeview", &tree_view, + NULL)) + return; + + app = terminal_app_get (); + terminal_util_bind_object_property_to_widget (G_OBJECT (app), TERMINAL_APP_ENABLE_MNEMONICS, + disable_mnemonics_button, 0); + terminal_util_bind_object_property_to_widget (G_OBJECT (app), TERMINAL_APP_ENABLE_MENU_BAR_ACCEL, + disable_menu_accel_button, 0); + + /* Column 1 */ + cell_renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("_Action"), + cell_renderer, + "text", ACTION_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column); + gtk_tree_view_column_set_sort_column_id (column, ACTION_COLUMN); + + /* Column 2 */ + cell_renderer = gtk_cell_renderer_accel_new (); + g_object_set (cell_renderer, + "editable", TRUE, + "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_GTK, + NULL); + g_signal_connect (cell_renderer, "accel-edited", + G_CALLBACK (accel_edited_callback), tree_view); + g_signal_connect (cell_renderer, "accel-cleared", + G_CALLBACK (accel_cleared_callback), tree_view); + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("Shortcut _Key")); + gtk_tree_view_column_pack_start (column, cell_renderer, TRUE); + gtk_tree_view_column_set_cell_data_func (column, cell_renderer, accel_set_func, NULL, NULL); + gtk_tree_view_column_set_sort_column_id (column, KEYVAL_COLUMN); + gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column); + + /* Add the data */ + + tree = edit_keys_store = gtk_tree_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER); + +#ifdef MATE_ENABLE_DEBUG + _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_ACCELS) + g_signal_connect (tree, "row-changed", G_CALLBACK (row_changed), NULL); +#endif + + for (i = 0; i < G_N_ELEMENTS (all_entries); ++i) + { + GtkTreeIter parent_iter; + guint j; + + gtk_tree_store_append (tree, &parent_iter, NULL); + gtk_tree_store_set (tree, &parent_iter, + ACTION_COLUMN, _(all_entries[i].user_visible_name), + -1); + + for (j = 0; j < all_entries[i].n_elements; ++j) + { + KeyEntry *key_entry = &(all_entries[i].key_entry[j]); + GtkTreeIter iter; + + gtk_tree_store_insert_with_values (tree, &iter, &parent_iter, -1, + ACTION_COLUMN, _(key_entry->user_visible_name), + KEYVAL_COLUMN, key_entry, + -1); + } + } + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (tree), + KEYVAL_COLUMN, accel_compare_func, + NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree), ACTION_COLUMN, + GTK_SORT_ASCENDING); + + gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (tree)); + g_object_unref (tree); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + g_signal_connect (notification_group, "accel-changed", + G_CALLBACK (treeview_accel_changed_cb), tree); + + edit_keys_dialog = dialog; + g_signal_connect (dialog, "destroy", + G_CALLBACK (edit_keys_dialog_destroy_cb), tree); + g_signal_connect (dialog, "response", + G_CALLBACK (edit_keys_dialog_response_cb), + NULL); + gtk_window_set_default_size (GTK_WINDOW (dialog), -1, 350); + +done: + gtk_window_set_transient_for (GTK_WINDOW (edit_keys_dialog), transient_parent); + gtk_window_present (GTK_WINDOW (edit_keys_dialog)); +} diff --git a/src/terminal-accels.h b/src/terminal-accels.h new file mode 100644 index 0000000..ee2cc6a --- /dev/null +++ b/src/terminal-accels.h @@ -0,0 +1,35 @@ +/* + * Copyright © 2001 Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TERMINAL_ACCELS_H +#define TERMINAL_ACCELS_H + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +void terminal_accels_init (void); + +void terminal_accels_shutdown (void); + +void terminal_edit_keys_dialog_show (GtkWindow *transient_parent); + +G_END_DECLS + +#endif /* TERMINAL_ACCELS_H */ diff --git a/src/terminal-app.c b/src/terminal-app.c new file mode 100644 index 0000000..aa3ede7 --- /dev/null +++ b/src/terminal-app.c @@ -0,0 +1,2116 @@ +/* + * Copyright © 2001, 2002 Havoc Pennington + * Copyright © 2002 Red Hat, Inc. + * Copyright © 2002 Sun Microsystems + * Copyright © 2003 Mariano Suarez-Alvarez + * Copyright © 2008 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <errno.h> + +#include <glib.h> + +#include "terminal-intl.h" + +#include "terminal-debug.h" +#include "terminal-app.h" +#include "terminal-accels.h" +#include "terminal-screen.h" +#include "terminal-screen-container.h" +#include "terminal-window.h" +#include "terminal-util.h" +#include "profile-editor.h" +#include "terminal-encoding.h" +#include <mateconf/mateconf-client.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> + +#ifdef WITH_SMCLIENT +#include "eggsmclient.h" +#ifdef GDK_WINDOWING_X11 +#include "eggdesktopfile.h" +#endif +#endif + +#define FALLBACK_PROFILE_ID "Default" + +/* Settings storage works as follows: + * /apps/mate-terminal/global/ + * /apps/mate-terminal/profiles/Foo/ + * + * It's somewhat tricky to manage the profiles/ dir since we need to track + * the list of profiles, but mateconf doesn't have a concept of notifying that + * a directory has appeared or disappeared. + * + * Session state is stored entirely in the RestartCommand command line. + * + * The number one rule: all stored information is EITHER per-session, + * per-profile, or set from a command line option. THERE CAN BE NO + * OVERLAP. The UI and implementation totally break if you overlap + * these categories. See mate-terminal 1.x for why. + * + * Don't use this code as an example of how to use MateConf - it's hugely + * overcomplicated due to the profiles stuff. Most apps should not + * have to do scary things of this nature, and should not have + * a profiles feature. + * + */ + +struct _TerminalAppClass { + GObjectClass parent_class; + + void (* quit) (TerminalApp *app); + void (* profile_list_changed) (TerminalApp *app); + void (* encoding_list_changed) (TerminalApp *app); +}; + +struct _TerminalApp +{ + GObject parent_instance; + + GList *windows; + GtkWidget *new_profile_dialog; + GtkWidget *manage_profiles_dialog; + GtkWidget *manage_profiles_list; + GtkWidget *manage_profiles_new_button; + GtkWidget *manage_profiles_edit_button; + GtkWidget *manage_profiles_delete_button; + GtkWidget *manage_profiles_default_menu; + + MateConfClient *conf; + guint profile_list_notify_id; + guint default_profile_notify_id; + guint encoding_list_notify_id; + guint system_font_notify_id; + guint enable_mnemonics_notify_id; + guint enable_menu_accels_notify_id; + + GHashTable *profiles; + char* default_profile_id; + TerminalProfile *default_profile; + gboolean default_profile_locked; + + GHashTable *encodings; + gboolean encodings_locked; + + PangoFontDescription *system_font_desc; + gboolean enable_mnemonics; + gboolean enable_menu_accels; +}; + +enum +{ + PROP_0, + PROP_DEFAULT_PROFILE, + PROP_ENABLE_MENU_BAR_ACCEL, + PROP_ENABLE_MNEMONICS, + PROP_SYSTEM_FONT, +}; + +enum +{ + QUIT, + PROFILE_LIST_CHANGED, + ENCODING_LIST_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +enum +{ + COL_PROFILE, + NUM_COLUMNS +}; + +enum +{ + SOURCE_DEFAULT = 0, + SOURCE_SESSION = 1 +}; + +static TerminalApp *global_app = NULL; + +/* Evil hack alert: this is exported from libmateconf-2 but not in a public header */ +extern gboolean mateconf_spawn_daemon(GError** err); + +#define MONOSPACE_FONT_DIR "/desktop/mate/interface" +#define MONOSPACE_FONT_KEY MONOSPACE_FONT_DIR "/monospace_font_name" +#define DEFAULT_MONOSPACE_FONT ("Monospace 10") + +#define ENABLE_MNEMONICS_KEY CONF_GLOBAL_PREFIX "/use_mnemonics" +#define DEFAULT_ENABLE_MNEMONICS (TRUE) + +#define ENABLE_MENU_BAR_ACCEL_KEY CONF_GLOBAL_PREFIX"/use_menu_accelerators" +#define DEFAULT_ENABLE_MENU_BAR_ACCEL (TRUE) + +#define PROFILE_LIST_KEY CONF_GLOBAL_PREFIX "/profile_list" +#define DEFAULT_PROFILE_KEY CONF_GLOBAL_PREFIX "/default_profile" + +#define ENCODING_LIST_KEY CONF_GLOBAL_PREFIX "/active_encodings" + +/* Helper functions */ + +static GdkScreen* +terminal_app_get_screen_by_display_name (const char *display_name, + int screen_number) +{ + GdkDisplay *display = NULL; + GdkScreen *screen = NULL; + + /* --screen=screen_number overrides --display */ + + if (display_name == NULL) + display = gdk_display_get_default (); + else + { + GSList *displays, *l; + const char *period; + + period = strrchr (display_name, '.'); + if (period) + { + gulong n; + char *end; + + errno = 0; + end = NULL; + n = g_ascii_strtoull (period + 1, &end, 0); + if (errno == 0 && (period + 1) != end) + screen_number = n; + } + + displays = gdk_display_manager_list_displays (gdk_display_manager_get ()); + for (l = displays; l != NULL; l = l->next) + { + GdkDisplay *disp = l->data; + + /* compare without the screen number part, if present */ + if ((period && strncmp (gdk_display_get_name (disp), display_name, period - display_name) == 0) || + (period == NULL && strcmp (gdk_display_get_name (disp), display_name) == 0)) + { + display = disp; + break; + } + } + g_slist_free (displays); + + if (display == NULL) + display = gdk_display_open (display_name); /* FIXME we never close displays */ + } + + if (display == NULL) + return NULL; + if (screen_number >= 0) + screen = gdk_display_get_screen (display, screen_number); + if (screen == NULL) + screen = gdk_display_get_default_screen (display); + + return screen; +} + +/* Menubar mnemonics settings handling */ + +static int +profiles_alphabetic_cmp (gconstpointer pa, + gconstpointer pb) +{ + TerminalProfile *a = (TerminalProfile *) pa; + TerminalProfile *b = (TerminalProfile *) pb; + int result; + + result = g_utf8_collate (terminal_profile_get_property_string (a, TERMINAL_PROFILE_VISIBLE_NAME), + terminal_profile_get_property_string (b, TERMINAL_PROFILE_VISIBLE_NAME)); + if (result == 0) + result = strcmp (terminal_profile_get_property_string (a, TERMINAL_PROFILE_NAME), + terminal_profile_get_property_string (b, TERMINAL_PROFILE_NAME)); + + return result; +} + +typedef struct +{ + TerminalProfile *result; + const char *target; +} LookupInfo; + +static void +profiles_lookup_by_visible_name_foreach (gpointer key, + gpointer value, + gpointer data) +{ + LookupInfo *info = data; + const char *name; + + name = terminal_profile_get_property_string (value, TERMINAL_PROFILE_VISIBLE_NAME); + if (name && strcmp (info->target, name) == 0) + info->result = value; +} + +static void +terminal_window_destroyed (TerminalWindow *window, + TerminalApp *app) +{ + app->windows = g_list_remove (app->windows, window); + + if (app->windows == NULL) + g_signal_emit (app, signals[QUIT], 0); +} + +static TerminalProfile * +terminal_app_create_profile (TerminalApp *app, + const char *name) +{ + TerminalProfile *profile; + + g_assert (terminal_app_get_profile_by_name (app, name) == NULL); + + profile = _terminal_profile_new (name); + + g_hash_table_insert (app->profiles, + g_strdup (terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME)), + profile /* adopts the refcount */); + + if (app->default_profile == NULL && + app->default_profile_id != NULL && + strcmp (app->default_profile_id, + terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME)) == 0) + { + /* We are the default profile */ + app->default_profile = profile; + g_object_notify (G_OBJECT (app), TERMINAL_APP_DEFAULT_PROFILE); + } + + return profile; +} + +static void +terminal_app_delete_profile (TerminalApp *app, + TerminalProfile *profile) +{ + GHashTableIter iter; + GSList *name_list; + const char *name, *profile_name; + char *mateconf_dir; + GError *error = NULL; + const char **nameptr = &name; + + profile_name = terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME); + mateconf_dir = mateconf_concat_dir_and_key (CONF_PREFIX "/profiles", profile_name); + + name_list = NULL; + g_hash_table_iter_init (&iter, app->profiles); + while (g_hash_table_iter_next (&iter, (gpointer *) nameptr, NULL)) + { + if (strcmp (name, profile_name) == 0) + continue; + + name_list = g_slist_prepend (name_list, g_strdup (name)); + } + + mateconf_client_set_list (app->conf, + CONF_GLOBAL_PREFIX"/profile_list", + MATECONF_VALUE_STRING, + name_list, + NULL); + + g_slist_foreach (name_list, (GFunc) g_free, NULL); + g_slist_free (name_list); + + /* And remove the profile directory */ + if (!mateconf_client_recursive_unset (app->conf, mateconf_dir, MATECONF_UNSET_INCLUDING_SCHEMA_NAMES, &error)) + { + g_warning ("Failed to recursively unset %s: %s\n", mateconf_dir, error->message); + g_error_free (error); + } + + g_free (mateconf_dir); +} + +static void +terminal_app_profile_cell_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + TerminalProfile *profile; + GValue value = { 0, }; + + gtk_tree_model_get (tree_model, iter, (int) COL_PROFILE, &profile, (int) -1); + + g_value_init (&value, G_TYPE_STRING); + g_object_get_property (G_OBJECT (profile), "visible-name", &value); + g_object_set_property (G_OBJECT (cell), "text", &value); + g_value_unset (&value); +} + +static int +terminal_app_profile_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + TerminalProfile *profile_a, *profile_b; + int retval; + + gtk_tree_model_get (model, a, (int) COL_PROFILE, &profile_a, (int) -1); + gtk_tree_model_get (model, b, (int) COL_PROFILE, &profile_b, (int) -1); + + retval = profiles_alphabetic_cmp (profile_a, profile_b); + + g_object_unref (profile_a); + g_object_unref (profile_b); + + return retval; +} + +static /* ref */ GtkTreeModel * +terminal_app_get_profile_liststore (TerminalApp *app, + TerminalProfile *selected_profile, + GtkTreeIter *selected_profile_iter, + gboolean *selected_profile_iter_set) +{ + GtkListStore *store; + GtkTreeIter iter; + GList *profiles, *l; + TerminalProfile *default_profile; + + store = gtk_list_store_new (NUM_COLUMNS, TERMINAL_TYPE_PROFILE); + + *selected_profile_iter_set = FALSE; + + if (selected_profile && + _terminal_profile_get_forgotten (selected_profile)) + selected_profile = NULL; + + profiles = terminal_app_get_profile_list (app); + default_profile = terminal_app_get_default_profile (app); + + for (l = profiles; l != NULL; l = l->next) + { + TerminalProfile *profile = TERMINAL_PROFILE (l->data); + + gtk_list_store_insert_with_values (store, &iter, 0, + (int) COL_PROFILE, profile, + (int) -1); + + if (selected_profile_iter && profile == selected_profile) + { + *selected_profile_iter = iter; + *selected_profile_iter_set = TRUE; + } + } + g_list_free (profiles); + + /* Now turn on sorting */ + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), + COL_PROFILE, + terminal_app_profile_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + COL_PROFILE, GTK_SORT_ASCENDING); + + return GTK_TREE_MODEL (store); +} + +static /* ref */ TerminalProfile* +profile_combo_box_get_selected (GtkWidget *widget) +{ + GtkComboBox *combo = GTK_COMBO_BOX (widget); + TerminalProfile *profile = NULL; + GtkTreeIter iter; + + if (gtk_combo_box_get_active_iter (combo, &iter)) + gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter, + (int) COL_PROFILE, &profile, (int) -1); + + return profile; +} + +static void +profile_combo_box_refill (TerminalApp *app, + GtkWidget *widget) +{ + GtkComboBox *combo = GTK_COMBO_BOX (widget); + GtkTreeIter iter; + gboolean iter_set; + TerminalProfile *selected_profile; + GtkTreeModel *model; + + selected_profile = profile_combo_box_get_selected (widget); + if (!selected_profile) + { + selected_profile = terminal_app_get_default_profile (app); + if (selected_profile) + g_object_ref (selected_profile); + } + + model = terminal_app_get_profile_liststore (app, + selected_profile, + &iter, + &iter_set); + gtk_combo_box_set_model (combo, model); + g_object_unref (model); + + if (iter_set) + gtk_combo_box_set_active_iter (combo, &iter); + + if (selected_profile) + g_object_unref (selected_profile); +} + +static GtkWidget* +profile_combo_box_new (TerminalApp *app) +{ + GtkWidget *combo; + GtkCellRenderer *renderer; + + combo = gtk_combo_box_new (); + terminal_util_set_atk_name_description (combo, NULL, _("Click button to choose profile")); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), renderer, + (GtkCellLayoutDataFunc) terminal_app_profile_cell_data_func, + NULL, NULL); + + profile_combo_box_refill (app, combo); + g_signal_connect (app, "profile-list-changed", + G_CALLBACK (profile_combo_box_refill), combo); + + gtk_widget_show (combo); + return combo; +} + +static void +profile_combo_box_changed_cb (GtkWidget *widget, + TerminalApp *app) +{ + TerminalProfile *profile; + + profile = profile_combo_box_get_selected (widget); + if (!profile) + return; + + mateconf_client_set_string (app->conf, + CONF_GLOBAL_PREFIX "/default_profile", + terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME), + NULL); + + /* Even though the mateconf change notification does this, it happens too late. + * In some cases, the default profile changes twice in quick succession, + * and update_default_profile must be called in sync with those changes. + */ + app->default_profile = profile; + + g_object_notify (G_OBJECT (app), TERMINAL_APP_DEFAULT_PROFILE); + + g_object_unref (profile); +} + +static void +profile_list_treeview_refill (TerminalApp *app, + GtkWidget *widget) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeIter iter; + gboolean iter_set; + GtkTreeSelection *selection; + GtkTreeModel *model; + TerminalProfile *selected_profile = NULL; + + model = gtk_tree_view_get_model (tree_view); + + selection = gtk_tree_view_get_selection (tree_view); + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + gtk_tree_model_get (model, &iter, (int) COL_PROFILE, &selected_profile, (int) -1); + + model = terminal_app_get_profile_liststore (terminal_app_get (), + selected_profile, + &iter, + &iter_set); + gtk_tree_view_set_model (tree_view, model); + g_object_unref (model); + + if (!iter_set) + iter_set = gtk_tree_model_get_iter_first (model, &iter); + + if (iter_set) + gtk_tree_selection_select_iter (selection, &iter); + + if (selected_profile) + g_object_unref (selected_profile); +} + +static GtkWidget* +profile_list_treeview_create (TerminalApp *app) +{ + GtkWidget *tree_view; + GtkTreeSelection *selection; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + tree_view = gtk_tree_view_new (); + terminal_util_set_atk_name_description (tree_view, _("Profile list"), NULL); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); + gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection), + GTK_SELECTION_BROWSE); + + column = gtk_tree_view_column_new (); + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer, TRUE); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer, + (GtkCellLayoutDataFunc) terminal_app_profile_cell_data_func, + NULL, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), + GTK_TREE_VIEW_COLUMN (column)); + + return tree_view; +} + +static void +profile_list_delete_confirm_response_cb (GtkWidget *dialog, + int response, + TerminalApp *app) +{ + TerminalProfile *profile; + + profile = TERMINAL_PROFILE (g_object_get_data (G_OBJECT (dialog), "profile")); + g_assert (profile != NULL); + + if (response == GTK_RESPONSE_ACCEPT) + terminal_app_delete_profile (app, profile); + + gtk_widget_destroy (dialog); +} + +static void +profile_list_delete_button_clicked_cb (GtkWidget *button, + GtkWidget *widget) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + TerminalApp *app = terminal_app_get (); + GtkTreeSelection *selection; + GtkWidget *dialog; + GtkTreeIter iter; + GtkTreeModel *model; + TerminalProfile *selected_profile; + GtkWidget *transient_parent; + + model = gtk_tree_view_get_model (tree_view); + selection = gtk_tree_view_get_selection (tree_view); + + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) + return; + + gtk_tree_model_get (model, &iter, (int) COL_PROFILE, &selected_profile, (int) -1); + + transient_parent = gtk_widget_get_toplevel (widget); + dialog = gtk_message_dialog_new (GTK_WINDOW (transient_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("Delete profile “%s”?"), + terminal_profile_get_property_string (selected_profile, TERMINAL_PROFILE_VISIBLE_NAME)); + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_DELETE, + GTK_RESPONSE_ACCEPT, + NULL); + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_REJECT, + -1); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), + GTK_RESPONSE_ACCEPT); + + gtk_window_set_title (GTK_WINDOW (dialog), _("Delete Profile")); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + /* Transfer refcount of |selected_profile|, so no unref below */ + g_object_set_data_full (G_OBJECT (dialog), "profile", selected_profile, g_object_unref); + + g_signal_connect (dialog, "response", + G_CALLBACK (profile_list_delete_confirm_response_cb), + app); + + gtk_window_present (GTK_WINDOW (dialog)); +} + +static void +profile_list_new_button_clicked_cb (GtkWidget *button, + gpointer data) +{ + TerminalApp *app; + + app = terminal_app_get (); + terminal_app_new_profile (app, NULL, GTK_WINDOW (app->manage_profiles_dialog)); +} + +static void +profile_list_edit_button_clicked_cb (GtkWidget *button, + GtkWidget *widget) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + GtkTreeSelection *selection; + GtkTreeIter iter; + GtkTreeModel *model; + TerminalProfile *selected_profile; + TerminalApp *app; + + app = terminal_app_get (); + + model = gtk_tree_view_get_model (tree_view); + selection = gtk_tree_view_get_selection (tree_view); + + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) + return; + + gtk_tree_model_get (model, &iter, (int) COL_PROFILE, &selected_profile, (int) -1); + + terminal_app_edit_profile (app, selected_profile, + GTK_WINDOW (app->manage_profiles_dialog), + NULL); + g_object_unref (selected_profile); +} + +static void +profile_list_row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data) +{ + GtkTreeIter iter; + GtkTreeModel *model; + TerminalProfile *selected_profile; + TerminalApp *app; + + app = terminal_app_get (); + + model = gtk_tree_view_get_model (tree_view); + + if (!gtk_tree_model_get_iter (model, &iter, path)) + return; + + gtk_tree_model_get (model, &iter, (int) COL_PROFILE, &selected_profile, (int) -1); + + terminal_app_edit_profile (app, selected_profile, + GTK_WINDOW (app->manage_profiles_dialog), + NULL); + g_object_unref (selected_profile); +} + +static GList* +find_profile_link (GList *profiles, + const char *name) +{ + GList *l; + + for (l = profiles; l != NULL; l = l->next) + { + const char *profile_name; + + profile_name = terminal_profile_get_property_string (TERMINAL_PROFILE (l->data), TERMINAL_PROFILE_NAME); + if (profile_name && strcmp (profile_name, name) == 0) + break; + } + + return l; +} + +static void +terminal_app_profile_list_notify_cb (MateConfClient *conf, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + TerminalApp *app = TERMINAL_APP (user_data); + GObject *object = G_OBJECT (app); + MateConfValue *val; + GSList *value_list, *sl; + GList *profiles_to_delete, *l; + gboolean need_new_default; + TerminalProfile *fallback; + guint count; + + g_object_freeze_notify (object); + + profiles_to_delete = terminal_app_get_profile_list (app); + + val = mateconf_entry_get_value (entry); + if (val == NULL || + val->type != MATECONF_VALUE_LIST || + mateconf_value_get_list_type (val) != MATECONF_VALUE_STRING) + goto ensure_one_profile; + + value_list = mateconf_value_get_list (val); + + /* Add any new ones */ + for (sl = value_list; sl != NULL; sl = sl->next) + { + MateConfValue *listvalue = (MateConfValue *) (sl->data); + const char *profile_name; + GList *link; + + profile_name = mateconf_value_get_string (listvalue); + if (!profile_name) + continue; + + link = find_profile_link (profiles_to_delete, profile_name); + if (link) + /* make profiles_to_delete point to profiles we didn't find in the list */ + profiles_to_delete = g_list_delete_link (profiles_to_delete, link); + else + terminal_app_create_profile (app, profile_name); + } + +ensure_one_profile: + + fallback = NULL; + count = g_hash_table_size (app->profiles); + if (count == 0 || count <= g_list_length (profiles_to_delete)) + { + /* We are going to run out, so create the fallback + * to be sure we always have one. Must be done + * here before we emit "forgotten" signals so that + * screens have a profile to fall back to. + * + * If the profile with the FALLBACK_ID already exists, + * we aren't allowed to delete it, unless at least one + * other profile will still exist. And if you delete + * all profiles, the FALLBACK_ID profile returns as + * the living dead. + */ + fallback = terminal_app_get_profile_by_name (app, FALLBACK_PROFILE_ID); + if (fallback == NULL) + fallback = terminal_app_create_profile (app, FALLBACK_PROFILE_ID); + g_assert (fallback != NULL); + } + + /* Forget no-longer-existing profiles */ + need_new_default = FALSE; + for (l = profiles_to_delete; l != NULL; l = l->next) + { + TerminalProfile *profile = TERMINAL_PROFILE (l->data); + const char *name; + + name = terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME); + if (strcmp (name, FALLBACK_PROFILE_ID) == 0) + continue; + + if (profile == app->default_profile) + { + app->default_profile = NULL; + need_new_default = TRUE; + } + + _terminal_profile_forget (profile); + g_hash_table_remove (app->profiles, name); + + /* |profile| possibly isn't dead yet since the profiles dialogue's tree model holds a ref too... */ + } + g_list_free (profiles_to_delete); + + if (need_new_default) + { + TerminalProfile *new_default; + TerminalProfile **new_default_ptr = &new_default; + + new_default = terminal_app_get_profile_by_name (app, FALLBACK_PROFILE_ID); + if (new_default == NULL) + { + GHashTableIter iter; + + g_hash_table_iter_init (&iter, app->profiles); + if (!g_hash_table_iter_next (&iter, NULL, (gpointer *) new_default_ptr)) + /* shouldn't really happen ever, but just to be safe */ + new_default = terminal_app_create_profile (app, FALLBACK_PROFILE_ID); + } + g_assert (new_default != NULL); + + app->default_profile = new_default; + + g_object_notify (object, TERMINAL_APP_DEFAULT_PROFILE); + } + + g_assert (g_hash_table_size (app->profiles) > 0); + + g_signal_emit (app, signals[PROFILE_LIST_CHANGED], 0); + + g_object_thaw_notify (object); +} + +static void +terminal_app_default_profile_notify_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + TerminalApp *app = TERMINAL_APP (user_data); + MateConfValue *val; + const char *name = NULL; + + app->default_profile_locked = !mateconf_entry_get_is_writable (entry); + + val = mateconf_entry_get_value (entry); + if (val != NULL && + val->type == MATECONF_VALUE_STRING) + name = mateconf_value_get_string (val); + if (!name || !name[0]) + name = FALLBACK_PROFILE_ID; + g_assert (name != NULL); + + g_free (app->default_profile_id); + app->default_profile_id = g_strdup (name); + + app->default_profile = terminal_app_get_profile_by_name (app, name); + + g_object_notify (G_OBJECT (app), TERMINAL_APP_DEFAULT_PROFILE); +} + +static int +compare_encodings (TerminalEncoding *a, + TerminalEncoding *b) +{ + return g_utf8_collate (a->name, b->name); +} + +static void +encoding_mark_active (gpointer key, + gpointer value, + gpointer data) +{ + TerminalEncoding *encoding = (TerminalEncoding *) value; + guint active = GPOINTER_TO_UINT (data); + + encoding->is_active = active; +} + +static void +terminal_app_encoding_list_notify_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + TerminalApp *app = TERMINAL_APP (user_data); + MateConfValue *val; + GSList *strings, *tmp; + TerminalEncoding *encoding; + const char *charset; + + app->encodings_locked = !mateconf_entry_get_is_writable (entry); + + /* Mark all as non-active, then re-enable the active ones */ + g_hash_table_foreach (app->encodings, (GHFunc) encoding_mark_active, GUINT_TO_POINTER (FALSE)); + + /* First add the locale's charset */ + encoding = g_hash_table_lookup (app->encodings, "current"); + g_assert (encoding); + if (terminal_encoding_is_valid (encoding)) + encoding->is_active = TRUE; + + /* Also always make UTF-8 available */ + encoding = g_hash_table_lookup (app->encodings, "UTF-8"); + g_assert (encoding); + if (terminal_encoding_is_valid (encoding)) + encoding->is_active = TRUE; + + val = mateconf_entry_get_value (entry); + if (val != NULL && + val->type == MATECONF_VALUE_LIST && + mateconf_value_get_list_type (val) == MATECONF_VALUE_STRING) + strings = mateconf_value_get_list (val); + else + strings = NULL; + + for (tmp = strings; tmp != NULL; tmp = tmp->next) + { + MateConfValue *v = (MateConfValue *) tmp->data; + + charset = mateconf_value_get_string (v); + if (!charset) + continue; + + encoding = terminal_app_ensure_encoding (app, charset); + if (!terminal_encoding_is_valid (encoding)) + continue; + + encoding->is_active = TRUE; + } + + g_signal_emit (app, signals[ENCODING_LIST_CHANGED], 0); +} + +static void +terminal_app_system_font_notify_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + TerminalApp *app = TERMINAL_APP (user_data); + MateConfValue *mateconf_value; + const char *font = NULL; + PangoFontDescription *font_desc; + + if (strcmp (mateconf_entry_get_key (entry), MONOSPACE_FONT_KEY) != 0) + return; + + mateconf_value = mateconf_entry_get_value (entry); + if (mateconf_value && + mateconf_value->type == MATECONF_VALUE_STRING) + font = mateconf_value_get_string (mateconf_value); + if (!font) + font = DEFAULT_MONOSPACE_FONT; + g_assert (font != NULL); + + font_desc = pango_font_description_from_string (font); + if (app->system_font_desc && + pango_font_description_equal (app->system_font_desc, font_desc)) + { + pango_font_description_free (font_desc); + return; + } + + if (app->system_font_desc) + pango_font_description_free (app->system_font_desc); + + app->system_font_desc = font_desc; + + g_object_notify (G_OBJECT (app), TERMINAL_APP_SYSTEM_FONT); +} + +static void +terminal_app_enable_mnemonics_notify_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + TerminalApp *app = TERMINAL_APP (user_data); + MateConfValue *mateconf_value; + gboolean enable; + + if (strcmp (mateconf_entry_get_key (entry), ENABLE_MNEMONICS_KEY) != 0) + return; + + mateconf_value = mateconf_entry_get_value (entry); + if (mateconf_value && + mateconf_value->type == MATECONF_VALUE_BOOL) + enable = mateconf_value_get_bool (mateconf_value); + else + enable = TRUE; + + if (enable == app->enable_mnemonics) + return; + + app->enable_mnemonics = enable; + g_object_notify (G_OBJECT (app), TERMINAL_APP_ENABLE_MNEMONICS); +} + +static void +terminal_app_enable_menu_accels_notify_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + TerminalApp *app = TERMINAL_APP (user_data); + MateConfValue *mateconf_value; + gboolean enable; + + if (strcmp (mateconf_entry_get_key (entry), ENABLE_MENU_BAR_ACCEL_KEY) != 0) + return; + + mateconf_value = mateconf_entry_get_value (entry); + if (mateconf_value && + mateconf_value->type == MATECONF_VALUE_BOOL) + enable = mateconf_value_get_bool (mateconf_value); + else + enable = TRUE; + + if (enable == app->enable_menu_accels) + return; + + app->enable_menu_accels = enable; + g_object_notify (G_OBJECT (app), TERMINAL_APP_ENABLE_MENU_BAR_ACCEL); +} + +static void +new_profile_response_cb (GtkWidget *new_profile_dialog, + int response_id, + TerminalApp *app) +{ + if (response_id == GTK_RESPONSE_ACCEPT) + { + GtkWidget *name_entry; + char *name; + const char *new_profile_name; + GtkWidget *base_option_menu; + TerminalProfile *base_profile = NULL; + TerminalProfile *new_profile; + GList *profiles; + GList *tmp; + GtkWindow *transient_parent; + GtkWidget *confirm_dialog; + gint retval; + GSList *list; + + base_option_menu = g_object_get_data (G_OBJECT (new_profile_dialog), "base_option_menu"); + base_profile = profile_combo_box_get_selected (base_option_menu); + if (!base_profile) + base_profile = terminal_app_get_default_profile (app); + if (!base_profile) + return; /* shouldn't happen ever though */ + + name_entry = g_object_get_data (G_OBJECT (new_profile_dialog), "name_entry"); + name = gtk_editable_get_chars (GTK_EDITABLE (name_entry), 0, -1); + g_strstrip (name); /* name will be non empty after stripping */ + + profiles = terminal_app_get_profile_list (app); + for (tmp = profiles; tmp != NULL; tmp = tmp->next) + { + TerminalProfile *profile = tmp->data; + const char *visible_name; + + visible_name = terminal_profile_get_property_string (profile, TERMINAL_PROFILE_VISIBLE_NAME); + + if (visible_name && strcmp (name, visible_name) == 0) + break; + } + if (tmp) + { + confirm_dialog = gtk_message_dialog_new (GTK_WINDOW (new_profile_dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + _("You already have a profile called “%s”. Do you want to create another profile with the same name?"), name); + /* Alternative button order was set automatically by GtkMessageDialog */ + retval = gtk_dialog_run (GTK_DIALOG (confirm_dialog)); + gtk_widget_destroy (confirm_dialog); + if (retval == GTK_RESPONSE_NO) + goto cleanup; + } + g_list_free (profiles); + + transient_parent = gtk_window_get_transient_for (GTK_WINDOW (new_profile_dialog)); + + new_profile = _terminal_profile_clone (base_profile, name); + new_profile_name = terminal_profile_get_property_string (new_profile, TERMINAL_PROFILE_NAME); + g_hash_table_insert (app->profiles, + g_strdup (new_profile_name), + new_profile /* adopts the refcount */); + + /* And now save the list to mateconf */ + list = mateconf_client_get_list (app->conf, + CONF_GLOBAL_PREFIX"/profile_list", + MATECONF_VALUE_STRING, + NULL); + list = g_slist_append (list, g_strdup (new_profile_name)); + mateconf_client_set_list (app->conf, + CONF_GLOBAL_PREFIX"/profile_list", + MATECONF_VALUE_STRING, + list, + NULL); + + terminal_profile_edit (new_profile, transient_parent, NULL); + + cleanup: + g_free (name); + } + + gtk_widget_destroy (new_profile_dialog); +} + +static void +new_profile_dialog_destroy_cb (GtkWidget *new_profile_dialog, + TerminalApp *app) +{ + GtkWidget *combo; + + combo = g_object_get_data (G_OBJECT (new_profile_dialog), "base_option_menu"); + g_signal_handlers_disconnect_by_func (app, G_CALLBACK (profile_combo_box_refill), combo); + + app->new_profile_dialog = NULL; +} + +static void +new_profile_name_entry_changed_cb (GtkEntry *entry, + GtkDialog *dialog) +{ + const char *name; + + name = gtk_entry_get_text (entry); + + /* make the create button sensitive only if something other than space has been set */ + while (*name != '\0' && g_ascii_isspace (*name)) + ++name; + + gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_ACCEPT, name[0] != '\0'); +} + +void +terminal_app_new_profile (TerminalApp *app, + TerminalProfile *default_base_profile, + GtkWindow *transient_parent) +{ + if (app->new_profile_dialog == NULL) + { + GtkWidget *create_button, *table, *name_label, *name_entry, *base_label, *combo; + + if (!terminal_util_load_builder_file ("profile-new-dialog.ui", + "new-profile-dialog", &app->new_profile_dialog, + "new-profile-create-button", &create_button, + "new-profile-table", &table, + "new-profile-name-label", &name_label, + "new-profile-name-entry", &name_entry, + "new-profile-base-label", &base_label, + NULL)) + return; + + g_signal_connect (G_OBJECT (app->new_profile_dialog), "response", G_CALLBACK (new_profile_response_cb), app); + g_signal_connect (app->new_profile_dialog, "destroy", G_CALLBACK (new_profile_dialog_destroy_cb), app); + + g_object_set_data (G_OBJECT (app->new_profile_dialog), "create_button", create_button); + gtk_widget_set_sensitive (create_button, FALSE); + + /* the name entry */ + g_object_set_data (G_OBJECT (app->new_profile_dialog), "name_entry", name_entry); + g_signal_connect (name_entry, "changed", G_CALLBACK (new_profile_name_entry_changed_cb), app->new_profile_dialog); + gtk_entry_set_activates_default (GTK_ENTRY (name_entry), TRUE); + gtk_widget_grab_focus (name_entry); + + gtk_label_set_mnemonic_widget (GTK_LABEL (name_label), name_entry); + + /* the base profile option menu */ + combo = profile_combo_box_new (app); + gtk_table_attach_defaults (GTK_TABLE (table), combo, 1, 2, 1, 2); + g_object_set_data (G_OBJECT (app->new_profile_dialog), "base_option_menu", combo); + terminal_util_set_atk_name_description (combo, NULL, _("Choose base profile")); + + gtk_label_set_mnemonic_widget (GTK_LABEL (base_label), combo); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (app->new_profile_dialog), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); + gtk_dialog_set_default_response (GTK_DIALOG (app->new_profile_dialog), GTK_RESPONSE_ACCEPT); + gtk_dialog_set_response_sensitive (GTK_DIALOG (app->new_profile_dialog), GTK_RESPONSE_ACCEPT, FALSE); + } + + gtk_window_set_transient_for (GTK_WINDOW (app->new_profile_dialog), + transient_parent); + + gtk_window_present (GTK_WINDOW (app->new_profile_dialog)); +} + +static void +profile_list_selection_changed_cb (GtkTreeSelection *selection, + TerminalApp *app) +{ + gboolean selected; + + selected = gtk_tree_selection_get_selected (selection, NULL, NULL); + + gtk_widget_set_sensitive (app->manage_profiles_edit_button, selected); + gtk_widget_set_sensitive (app->manage_profiles_delete_button, + selected && + g_hash_table_size (app->profiles) > 1); +} + +static void +profile_list_response_cb (GtkWidget *dialog, + int id, + TerminalApp *app) +{ + g_assert (app->manage_profiles_dialog == dialog); + + if (id == GTK_RESPONSE_HELP) + { + terminal_util_show_help ("mate-terminal-manage-profiles", GTK_WINDOW (dialog)); + return; + } + + gtk_widget_destroy (dialog); +} + +static void +profile_list_destroyed_cb (GtkWidget *manage_profiles_dialog, + TerminalApp *app) +{ + g_signal_handlers_disconnect_by_func (app, G_CALLBACK (profile_list_treeview_refill), app->manage_profiles_list); + g_signal_handlers_disconnect_by_func (app, G_CALLBACK (profile_combo_box_refill), app->manage_profiles_default_menu); + + app->manage_profiles_dialog = NULL; + app->manage_profiles_list = NULL; + app->manage_profiles_new_button = NULL; + app->manage_profiles_edit_button = NULL; + app->manage_profiles_delete_button = NULL; + app->manage_profiles_default_menu = NULL; +} + +void +terminal_app_manage_profiles (TerminalApp *app, + GtkWindow *transient_parent) +{ + GObject *dialog; + GObject *tree_view_container, *new_button, *edit_button, *remove_button; + GObject *default_hbox, *default_label; + GtkTreeSelection *selection; + + if (app->manage_profiles_dialog) + { + gtk_window_set_transient_for (GTK_WINDOW (app->manage_profiles_dialog), transient_parent); + gtk_window_present (GTK_WINDOW (app->manage_profiles_dialog)); + return; + } + + if (!terminal_util_load_builder_file ("profile-manager.ui", + "profile-manager", &dialog, + "profiles-treeview-container", &tree_view_container, + "new-profile-button", &new_button, + "edit-profile-button", &edit_button, + "delete-profile-button", &remove_button, + "default-profile-hbox", &default_hbox, + "default-profile-label", &default_label, + NULL)) + return; + + app->manage_profiles_dialog = GTK_WIDGET (dialog); + app->manage_profiles_new_button = GTK_WIDGET (new_button); + app->manage_profiles_edit_button = GTK_WIDGET (edit_button); + app->manage_profiles_delete_button = GTK_WIDGET (remove_button); + + g_signal_connect (dialog, "response", G_CALLBACK (profile_list_response_cb), app); + g_signal_connect (dialog, "destroy", G_CALLBACK (profile_list_destroyed_cb), app); + + app->manage_profiles_list = profile_list_treeview_create (app); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (app->manage_profiles_list)); + g_signal_connect (selection, "changed", G_CALLBACK (profile_list_selection_changed_cb), app); + + profile_list_treeview_refill (app, app->manage_profiles_list); + g_signal_connect (app, "profile-list-changed", + G_CALLBACK (profile_list_treeview_refill), app->manage_profiles_list); + + g_signal_connect (app->manage_profiles_list, "row-activated", + G_CALLBACK (profile_list_row_activated_cb), app); + + gtk_container_add (GTK_CONTAINER (tree_view_container), app->manage_profiles_list); + gtk_widget_show (app->manage_profiles_list); + + g_signal_connect (new_button, "clicked", + G_CALLBACK (profile_list_new_button_clicked_cb), + app->manage_profiles_list); + g_signal_connect (edit_button, "clicked", + G_CALLBACK (profile_list_edit_button_clicked_cb), + app->manage_profiles_list); + g_signal_connect (remove_button, "clicked", + G_CALLBACK (profile_list_delete_button_clicked_cb), + app->manage_profiles_list); + + app->manage_profiles_default_menu = profile_combo_box_new (app); + g_signal_connect (app->manage_profiles_default_menu, "changed", + G_CALLBACK (profile_combo_box_changed_cb), app); + + gtk_box_pack_start (GTK_BOX (default_hbox), app->manage_profiles_default_menu, FALSE, FALSE, 0); + gtk_widget_show (app->manage_profiles_default_menu); + + gtk_label_set_mnemonic_widget (GTK_LABEL (default_label), app->manage_profiles_default_menu); + + gtk_widget_grab_focus (app->manage_profiles_list); + + gtk_window_set_transient_for (GTK_WINDOW (app->manage_profiles_dialog), + transient_parent); + + gtk_window_present (GTK_WINDOW (app->manage_profiles_dialog)); +} + +#ifdef WITH_SMCLIENT + +static void +terminal_app_save_state_cb (EggSMClient *client, + GKeyFile *key_file, + TerminalApp *app) +{ + terminal_app_save_config (app, key_file); +} + +static void +terminal_app_client_quit_cb (EggSMClient *client, + TerminalApp *app) +{ + g_signal_emit (app, signals[QUIT], 0); +} + +#endif /* WITH_SMCLIENT */ + +/* Class implementation */ + +G_DEFINE_TYPE (TerminalApp, terminal_app, G_TYPE_OBJECT) + +static void +terminal_app_init (TerminalApp *app) +{ + GError *error = NULL; + + global_app = app; + + /* If the mateconf daemon isn't available (e.g. because there's no dbus + * session bus running), we'd crash later on. Tell the user about it + * now, and exit. See bug #561663. + * Don't use mateconf_ping_daemon() here since the server may just not + * be running yet, but able to be started. See comments on bug #564649. + */ + if (!mateconf_spawn_daemon (&error)) + { + g_printerr ("Failed to summon the MateConf demon; exiting. %s\n", error->message); + g_error_free (error); + + exit (EXIT_FAILURE); + } + + gtk_window_set_default_icon_name (MATE_TERMINAL_ICON_NAME); + + /* Initialise defaults */ + app->enable_mnemonics = DEFAULT_ENABLE_MNEMONICS; + app->enable_menu_accels = DEFAULT_ENABLE_MENU_BAR_ACCEL; + + app->profiles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + + app->encodings = terminal_encodings_get_builtins (); + + app->conf = mateconf_client_get_default (); + + mateconf_client_add_dir (app->conf, CONF_GLOBAL_PREFIX, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + mateconf_client_add_dir (app->conf, MONOSPACE_FONT_DIR, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + mateconf_client_add_dir (app->conf, CONF_PROXY_PREFIX, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + mateconf_client_add_dir (app->conf, CONF_HTTP_PROXY_PREFIX, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + + app->profile_list_notify_id = + mateconf_client_notify_add (app->conf, PROFILE_LIST_KEY, + terminal_app_profile_list_notify_cb, + app, NULL, NULL); + + app->default_profile_notify_id = + mateconf_client_notify_add (app->conf, + DEFAULT_PROFILE_KEY, + terminal_app_default_profile_notify_cb, + app, NULL, NULL); + + app->encoding_list_notify_id = + mateconf_client_notify_add (app->conf, + ENCODING_LIST_KEY, + terminal_app_encoding_list_notify_cb, + app, NULL, NULL); + + app->system_font_notify_id = + mateconf_client_notify_add (app->conf, + MONOSPACE_FONT_KEY, + terminal_app_system_font_notify_cb, + app, NULL, NULL); + + app->enable_mnemonics_notify_id = + mateconf_client_notify_add (app->conf, + ENABLE_MNEMONICS_KEY, + terminal_app_enable_mnemonics_notify_cb, + app, NULL, NULL); + + app->enable_menu_accels_notify_id = + mateconf_client_notify_add (app->conf, + ENABLE_MENU_BAR_ACCEL_KEY, + terminal_app_enable_menu_accels_notify_cb, + app, NULL, NULL); + + /* Load the settings */ + mateconf_client_notify (app->conf, PROFILE_LIST_KEY); + mateconf_client_notify (app->conf, DEFAULT_PROFILE_KEY); + mateconf_client_notify (app->conf, ENCODING_LIST_KEY); + mateconf_client_notify (app->conf, MONOSPACE_FONT_KEY); + mateconf_client_notify (app->conf, ENABLE_MENU_BAR_ACCEL_KEY); + mateconf_client_notify (app->conf, ENABLE_MNEMONICS_KEY); + + /* Ensure we have valid settings */ + g_assert (app->default_profile_id != NULL); + g_assert (app->system_font_desc != NULL); + + terminal_accels_init (); + +#ifdef WITH_SMCLIENT +{ + EggSMClient *sm_client; +#ifdef GDK_WINDOWING_X11 + char *desktop_file; + + desktop_file = g_build_filename (TERM_DATADIR, + "applications", + PACKAGE ".desktop", + NULL); + egg_set_desktop_file_without_defaults (desktop_file); + g_free (desktop_file); +#endif /* GDK_WINDOWING_X11 */ + + sm_client = egg_sm_client_get (); + g_signal_connect (sm_client, "save-state", + G_CALLBACK (terminal_app_save_state_cb), app); + g_signal_connect (sm_client, "quit", + G_CALLBACK (terminal_app_client_quit_cb), app); +} +#endif +} + +static void +terminal_app_finalize (GObject *object) +{ + TerminalApp *app = TERMINAL_APP (object); + +#ifdef WITH_SMCLIENT + EggSMClient *sm_client; + + sm_client = egg_sm_client_get (); + g_signal_handlers_disconnect_matched (sm_client, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, app); +#endif + + if (app->profile_list_notify_id != 0) + mateconf_client_notify_remove (app->conf, app->profile_list_notify_id); + if (app->default_profile_notify_id != 0) + mateconf_client_notify_remove (app->conf, app->default_profile_notify_id); + if (app->encoding_list_notify_id != 0) + mateconf_client_notify_remove (app->conf, app->encoding_list_notify_id); + if (app->system_font_notify_id != 0) + mateconf_client_notify_remove (app->conf, app->system_font_notify_id); + if (app->enable_menu_accels_notify_id != 0) + mateconf_client_notify_remove (app->conf, app->enable_menu_accels_notify_id); + if (app->enable_mnemonics_notify_id != 0) + mateconf_client_notify_remove (app->conf, app->enable_mnemonics_notify_id); + + mateconf_client_remove_dir (app->conf, CONF_GLOBAL_PREFIX, NULL); + mateconf_client_remove_dir (app->conf, MONOSPACE_FONT_DIR, NULL); + + g_object_unref (app->conf); + + g_free (app->default_profile_id); + + g_hash_table_destroy (app->profiles); + + g_hash_table_destroy (app->encodings); + + pango_font_description_free (app->system_font_desc); + + terminal_accels_shutdown (); + + G_OBJECT_CLASS (terminal_app_parent_class)->finalize (object); + + global_app = NULL; +} + +static void +terminal_app_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TerminalApp *app = TERMINAL_APP (object); + + switch (prop_id) + { + case PROP_SYSTEM_FONT: + if (app->system_font_desc) + g_value_set_boxed (value, app->system_font_desc); + else + g_value_take_boxed (value, pango_font_description_from_string (DEFAULT_MONOSPACE_FONT)); + break; + case PROP_ENABLE_MENU_BAR_ACCEL: + g_value_set_boolean (value, app->enable_menu_accels); + break; + case PROP_ENABLE_MNEMONICS: + g_value_set_boolean (value, app->enable_mnemonics); + break; + case PROP_DEFAULT_PROFILE: + g_value_set_object (value, app->default_profile); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_app_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TerminalApp *app = TERMINAL_APP (object); + + switch (prop_id) + { + case PROP_ENABLE_MENU_BAR_ACCEL: + app->enable_menu_accels = g_value_get_boolean (value); + mateconf_client_set_bool (app->conf, ENABLE_MENU_BAR_ACCEL_KEY, app->enable_menu_accels, NULL); + break; + case PROP_ENABLE_MNEMONICS: + app->enable_mnemonics = g_value_get_boolean (value); + mateconf_client_set_bool (app->conf, ENABLE_MNEMONICS_KEY, app->enable_mnemonics, NULL); + break; + case PROP_DEFAULT_PROFILE: + case PROP_SYSTEM_FONT: + /* not writable */ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_app_real_quit (TerminalApp *app) +{ + gtk_main_quit(); +} + +static void +terminal_app_class_init (TerminalAppClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = terminal_app_finalize; + object_class->get_property = terminal_app_get_property; + object_class->set_property = terminal_app_set_property; + + klass->quit = terminal_app_real_quit; + + signals[QUIT] = + g_signal_new (I_("quit"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (TerminalAppClass, quit), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[PROFILE_LIST_CHANGED] = + g_signal_new (I_("profile-list-changed"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (TerminalAppClass, profile_list_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[ENCODING_LIST_CHANGED] = + g_signal_new (I_("encoding-list-changed"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (TerminalAppClass, profile_list_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_object_class_install_property + (object_class, + PROP_ENABLE_MENU_BAR_ACCEL, + g_param_spec_boolean (TERMINAL_APP_ENABLE_MENU_BAR_ACCEL, NULL, NULL, + DEFAULT_ENABLE_MENU_BAR_ACCEL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property + (object_class, + PROP_ENABLE_MNEMONICS, + g_param_spec_boolean (TERMINAL_APP_ENABLE_MNEMONICS, NULL, NULL, + DEFAULT_ENABLE_MNEMONICS, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property + (object_class, + PROP_SYSTEM_FONT, + g_param_spec_boxed (TERMINAL_APP_SYSTEM_FONT, NULL, NULL, + PANGO_TYPE_FONT_DESCRIPTION, + G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property + (object_class, + PROP_DEFAULT_PROFILE, + g_param_spec_object (TERMINAL_APP_DEFAULT_PROFILE, NULL, NULL, + TERMINAL_TYPE_PROFILE, + G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); +} + +/* Public API */ + +TerminalApp* +terminal_app_get (void) +{ + if (global_app == NULL) { + g_object_new (TERMINAL_TYPE_APP, NULL); + g_assert (global_app != NULL); + } + + return global_app; +} + +void +terminal_app_shutdown (void) +{ + if (global_app == NULL) + return; + + g_object_unref (global_app); + g_assert (global_app == NULL); +} + +/** + * terminal_app_handle_options: + * @app: + * @options: a #TerminalOptions + * @allow_resume: whether to merge the terminal configuration from the + * saved session on resume + * @error: a #GError to fill in + * + * Processes @options. It loads or saves the terminal configuration, or + * opens the specified windows and tabs. + * + * Returns: %TRUE if @options could be successfully handled, or %FALSE on + * error + */ +gboolean +terminal_app_handle_options (TerminalApp *app, + TerminalOptions *options, + gboolean allow_resume, + GError **error) +{ + GList *lw; + GdkScreen *gdk_screen; + + gdk_screen = terminal_app_get_screen_by_display_name (options->display_name, + options->screen_number); + + if (options->save_config) + { + if (options->remote_arguments) + return terminal_app_save_config_file (app, options->config_file, error); + + g_set_error_literal (error, TERMINAL_OPTION_ERROR, TERMINAL_OPTION_ERROR_NOT_IN_FACTORY, + "Cannot use \"--save-config\" when starting the factory process"); + return FALSE; + } + + if (options->load_config) + { + GKeyFile *key_file; + gboolean result; + + key_file = g_key_file_new (); + result = g_key_file_load_from_file (key_file, options->config_file, 0, error) && + terminal_options_merge_config (options, key_file, SOURCE_DEFAULT, error); + g_key_file_free (key_file); + + if (!result) + return FALSE; + + /* fall-through on success */ + } + +#ifdef WITH_SMCLIENT +{ + EggSMClient *sm_client; + + sm_client = egg_sm_client_get (); + + if (allow_resume && egg_sm_client_is_resumed (sm_client)) + { + GKeyFile *key_file; + + key_file = egg_sm_client_get_state_file (sm_client); + if (key_file != NULL && + !terminal_options_merge_config (options, key_file, SOURCE_SESSION, error)) + return FALSE; + } +} +#endif + + /* Make sure we open at least one window */ + terminal_options_ensure_window (options); + + if (options->startup_id != NULL) + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, + "Startup ID is '%s'\n", + options->startup_id); + + for (lw = options->initial_windows; lw != NULL; lw = lw->next) + { + InitialWindow *iw = lw->data; + TerminalWindow *window; + GList *lt; + + g_assert (iw->tabs); + + /* Create & setup new window */ + window = terminal_app_new_window (app, gdk_screen); + + /* Restored windows shouldn't demand attention; see bug #586308. */ + if (iw->source_tag == SOURCE_SESSION) + terminal_window_set_is_restored (window); + + if (options->startup_id != NULL) + gtk_window_set_startup_id (GTK_WINDOW (window), options->startup_id); + + /* Overwrite the default, unique window role set in terminal_window_init */ + if (iw->role) + gtk_window_set_role (GTK_WINDOW (window), iw->role); + + if (iw->force_menubar_state) + terminal_window_set_menubar_visible (window, iw->menubar_state); + + if (iw->start_fullscreen) + gtk_window_fullscreen (GTK_WINDOW (window)); + if (iw->start_maximized) + gtk_window_maximize (GTK_WINDOW (window)); + + /* Now add the tabs */ + for (lt = iw->tabs; lt != NULL; lt = lt->next) + { + InitialTab *it = lt->data; + TerminalProfile *profile = NULL; + TerminalScreen *screen; + const char *profile_name; + gboolean profile_is_id; + + if (it->profile) + { + profile_name = it->profile; + profile_is_id = it->profile_is_id; + } + else + { + profile_name = options->default_profile; + profile_is_id = options->default_profile_is_id; + } + + if (profile_name) + { + if (profile_is_id) + profile = terminal_app_get_profile_by_name (app, profile_name); + else + profile = terminal_app_get_profile_by_visible_name (app, profile_name); + + if (profile == NULL) + g_printerr (_("No such profile \"%s\", using default profile\n"), it->profile); + } + if (profile == NULL) + profile = terminal_app_get_profile_for_new_term (app); + g_assert (profile); + + screen = terminal_app_new_terminal (app, window, profile, + it->exec_argv ? it->exec_argv : options->exec_argv, + it->title ? it->title : options->default_title, + it->working_dir ? it->working_dir : options->default_working_dir, + options->env, + it->zoom_set ? it->zoom : options->zoom); + + if (it->active) + terminal_window_switch_screen (window, screen); + } + + if (iw->geometry) + { + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] applying geometry %s\n", + window, iw->geometry); + + if (!gtk_window_parse_geometry (GTK_WINDOW (window), iw->geometry)) + g_printerr (_("Invalid geometry string \"%s\"\n"), iw->geometry); + } + + gtk_window_present (GTK_WINDOW (window)); + } + + return TRUE; +} + +TerminalWindow * +terminal_app_new_window (TerminalApp *app, + GdkScreen *screen) +{ + TerminalWindow *window; + + window = terminal_window_new (); + + app->windows = g_list_append (app->windows, window); + g_signal_connect (window, "destroy", + G_CALLBACK (terminal_window_destroyed), app); + + if (screen) + gtk_window_set_screen (GTK_WINDOW (window), screen); + + return window; +} + +TerminalScreen * +terminal_app_new_terminal (TerminalApp *app, + TerminalWindow *window, + TerminalProfile *profile, + char **override_command, + const char *title, + const char *working_dir, + char **child_env, + double zoom) +{ + TerminalScreen *screen; + + g_return_val_if_fail (TERMINAL_IS_APP (app), NULL); + g_return_val_if_fail (TERMINAL_IS_WINDOW (window), NULL); + + screen = terminal_screen_new (profile, override_command, title, + working_dir, child_env, zoom); + + terminal_window_add_screen (window, screen, -1); + terminal_window_switch_screen (window, screen); + gtk_widget_grab_focus (GTK_WIDGET (screen)); + + return screen; +} + +void +terminal_app_edit_profile (TerminalApp *app, + TerminalProfile *profile, + GtkWindow *transient_parent, + const char *widget_name) +{ + terminal_profile_edit (profile, transient_parent, widget_name); +} + +void +terminal_app_edit_keybindings (TerminalApp *app, + GtkWindow *transient_parent) +{ + terminal_edit_keys_dialog_show (transient_parent); +} + +void +terminal_app_edit_encodings (TerminalApp *app, + GtkWindow *transient_parent) +{ + terminal_encoding_dialog_show (transient_parent); +} + +TerminalWindow * +terminal_app_get_current_window (TerminalApp *app) +{ + if (app->windows == NULL) + return NULL; + + return g_list_last (app->windows)->data; +} + +/** + * terminal_profile_get_list: + * + * Returns: a #GList containing all #TerminalProfile objects. + * The content of the list is owned by the backend and + * should not be modified or freed. Use g_list_free() when done + * using the list. + */ +GList* +terminal_app_get_profile_list (TerminalApp *app) +{ + g_return_val_if_fail (TERMINAL_IS_APP (app), NULL); + + return g_list_sort (g_hash_table_get_values (app->profiles), profiles_alphabetic_cmp); +} + +TerminalProfile* +terminal_app_get_profile_by_name (TerminalApp *app, + const char *name) +{ + g_return_val_if_fail (TERMINAL_IS_APP (app), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return g_hash_table_lookup (app->profiles, name); +} + +TerminalProfile* +terminal_app_get_profile_by_visible_name (TerminalApp *app, + const char *name) +{ + LookupInfo info; + + g_return_val_if_fail (TERMINAL_IS_APP (app), NULL); + g_return_val_if_fail (name != NULL, NULL); + + info.result = NULL; + info.target = name; + + g_hash_table_foreach (app->profiles, + profiles_lookup_by_visible_name_foreach, + &info); + return info.result; +} + +TerminalProfile* +terminal_app_get_default_profile (TerminalApp *app) +{ + g_return_val_if_fail (TERMINAL_IS_APP (app), NULL); + + return app->default_profile; +} + +TerminalProfile* +terminal_app_get_profile_for_new_term (TerminalApp *app) +{ + GHashTableIter iter; + TerminalProfile *profile = NULL; + TerminalProfile **profileptr = &profile; + + g_return_val_if_fail (TERMINAL_IS_APP (app), NULL); + + if (app->default_profile) + return app->default_profile; + + g_hash_table_iter_init (&iter, app->profiles); + if (g_hash_table_iter_next (&iter, NULL, (gpointer *) profileptr)) + return profile; + + return NULL; +} + +GHashTable * +terminal_app_get_encodings (TerminalApp *app) +{ + return app->encodings; +} + +/** + * terminal_app_ensure_encoding: + * @app: + * @charset: + * + * Ensures there's a #TerminalEncoding for @charset available. + */ +TerminalEncoding * +terminal_app_ensure_encoding (TerminalApp *app, + const char *charset) +{ + TerminalEncoding *encoding; + + encoding = g_hash_table_lookup (app->encodings, charset); + if (encoding == NULL) + { + encoding = terminal_encoding_new (charset, + _("User Defined"), + TRUE, + TRUE /* scary! */); + g_hash_table_insert (app->encodings, + (gpointer) terminal_encoding_get_id (encoding), + encoding); + } + + return encoding; +} + +/** + * terminal_app_get_active_encodings: + * + * Returns: a newly allocated list of newly referenced #TerminalEncoding objects. + */ +GSList* +terminal_app_get_active_encodings (TerminalApp *app) +{ + GSList *list = NULL; + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, app->encodings); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + TerminalEncoding *encoding = (TerminalEncoding *) value; + + if (!encoding->is_active) + continue; + + list = g_slist_prepend (list, terminal_encoding_ref (encoding)); + } + + return g_slist_sort (list, (GCompareFunc) compare_encodings); +} + +void +terminal_app_save_config (TerminalApp *app, + GKeyFile *key_file) +{ + GList *lw; + guint n = 0; + GPtrArray *window_names_array; + char **window_names; + gsize len; + + g_key_file_set_comment (key_file, NULL, NULL, "Written by " PACKAGE_STRING, NULL); + + g_key_file_set_integer (key_file, TERMINAL_CONFIG_GROUP, TERMINAL_CONFIG_PROP_VERSION, TERMINAL_CONFIG_VERSION); + g_key_file_set_integer (key_file, TERMINAL_CONFIG_GROUP, TERMINAL_CONFIG_PROP_COMPAT_VERSION, TERMINAL_CONFIG_COMPAT_VERSION); + + window_names_array = g_ptr_array_sized_new (g_list_length (app->windows) + 1); + + for (lw = app->windows; lw != NULL; lw = lw->next) + { + TerminalWindow *window = TERMINAL_WINDOW (lw->data); + char *group; + + group = g_strdup_printf ("Window%u", n++); + g_ptr_array_add (window_names_array, group); + + terminal_window_save_state (window, key_file, group); + } + + len = window_names_array->len; + g_ptr_array_add (window_names_array, NULL); + window_names = (char **) g_ptr_array_free (window_names_array, FALSE); + g_key_file_set_string_list (key_file, TERMINAL_CONFIG_GROUP, TERMINAL_CONFIG_PROP_WINDOWS, (const char * const *) window_names, len); + g_strfreev (window_names); +} + +gboolean +terminal_app_save_config_file (TerminalApp *app, + const char *file_name, + GError **error) +{ + GKeyFile *key_file; + char *data; + gsize len; + gboolean result; + + key_file = g_key_file_new (); + terminal_app_save_config (app, key_file); + + data = g_key_file_to_data (key_file, &len, NULL); + result = g_file_set_contents (file_name, data, len, error); + g_free (data); + + return result; +} diff --git a/src/terminal-app.h b/src/terminal-app.h new file mode 100644 index 0000000..0b90b2f --- /dev/null +++ b/src/terminal-app.h @@ -0,0 +1,159 @@ +/* + * Copyright © 2001 Havoc Pennington + * Copyright © 2008 Christian Persch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TERMINAL_APP_H +#define TERMINAL_APP_H + +#include <gtk/gtk.h> + +#include "terminal-encoding.h" +#include "terminal-screen.h" +#include "terminal-options.h" + +G_BEGIN_DECLS + +/* Terminal conf files */ + +#define TERMINAL_CONFIG_VERSION (1) /* Bump this for any changes */ +#define TERMINAL_CONFIG_COMPAT_VERSION (1) /* Bump this for incompatible changes */ + +#define TERMINAL_CONFIG_GROUP "MATE Terminal Configuration" +#define TERMINAL_CONFIG_PROP_VERSION "Version" +#define TERMINAL_CONFIG_PROP_COMPAT_VERSION "CompatVersion" +#define TERMINAL_CONFIG_PROP_WINDOWS "Windows" + +#define TERMINAL_CONFIG_WINDOW_PROP_ACTIVE_TAB "ActiveTerminal" +#define TERMINAL_CONFIG_WINDOW_PROP_FULLSCREEN "Fullscreen" +#define TERMINAL_CONFIG_WINDOW_PROP_GEOMETRY "Geometry" +#define TERMINAL_CONFIG_WINDOW_PROP_MAXIMIZED "Maximized" +#define TERMINAL_CONFIG_WINDOW_PROP_MENUBAR_VISIBLE "MenubarVisible" +#define TERMINAL_CONFIG_WINDOW_PROP_ROLE "Role" +#define TERMINAL_CONFIG_WINDOW_PROP_TABS "Terminals" + +#define TERMINAL_CONFIG_TERMINAL_PROP_HEIGHT "Height" +#define TERMINAL_CONFIG_TERMINAL_PROP_COMMAND "Command" +#define TERMINAL_CONFIG_TERMINAL_PROP_PROFILE_ID "ProfileID" +#define TERMINAL_CONFIG_TERMINAL_PROP_TITLE "Title" +#define TERMINAL_CONFIG_TERMINAL_PROP_WIDTH "Width" +#define TERMINAL_CONFIG_TERMINAL_PROP_WORKING_DIRECTORY "WorkingDirectory" +#define TERMINAL_CONFIG_TERMINAL_PROP_ZOOM "Zoom" + +/* Configuration */ + +#define CONF_PREFIX "/apps/mate-terminal" +#define CONF_GLOBAL_PREFIX CONF_PREFIX "/global" +#define CONF_PROFILES_PREFIX CONF_PREFIX "/profiles" +#define CONF_KEYS_PREFIX CONF_PREFIX "/keybindings" + +#define MATE_TERMINAL_ICON_NAME "utilities-terminal" + +#define TERMINAL_APP_DEFAULT_PROFILE "default-profile" +#define TERMINAL_APP_ENABLE_MENU_BAR_ACCEL "enable-menu-accels" +#define TERMINAL_APP_ENABLE_MNEMONICS "enable-mnemonics" +#define TERMINAL_APP_SYSTEM_FONT "system-font" + +/* TerminalApp */ + +#define TERMINAL_TYPE_APP (terminal_app_get_type ()) +#define TERMINAL_APP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TERMINAL_TYPE_APP, TerminalApp)) +#define TERMINAL_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TERMINAL_TYPE_APP, TerminalAppClass)) +#define TERMINAL_IS_APP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TERMINAL_TYPE_APP)) +#define TERMINAL_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TERMINAL_TYPE_APP)) +#define TERMINAL_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TERMINAL_TYPE_APP, TerminalAppClass)) + +typedef struct _TerminalAppClass TerminalAppClass; +typedef struct _TerminalApp TerminalApp; + +GType terminal_app_get_type (void); + +TerminalApp* terminal_app_get (void); + +void terminal_app_shutdown (void); + +gboolean terminal_app_handle_options (TerminalApp *app, + TerminalOptions *options, + gboolean allow_resume, + GError **error); + +void terminal_app_edit_profile (TerminalApp *app, + TerminalProfile *profile, + GtkWindow *transient_parent, + const char *widget_name); + +void terminal_app_new_profile (TerminalApp *app, + TerminalProfile *default_base_profile, + GtkWindow *transient_parent); + +TerminalWindow * terminal_app_new_window (TerminalApp *app, + GdkScreen *screen); + +TerminalScreen *terminal_app_new_terminal (TerminalApp *app, + TerminalWindow *window, + TerminalProfile *profile, + char **override_command, + const char *title, + const char *working_dir, + char **child_env, + double zoom); + +TerminalWindow *terminal_app_get_current_window (TerminalApp *app); + +void terminal_app_manage_profiles (TerminalApp *app, + GtkWindow *transient_parent); + +void terminal_app_edit_keybindings (TerminalApp *app, + GtkWindow *transient_parent); +void terminal_app_edit_encodings (TerminalApp *app, + GtkWindow *transient_parent); + + +GList* terminal_app_get_profile_list (TerminalApp *app); + +TerminalProfile* terminal_app_ensure_profile_fallback (TerminalApp *app); + +TerminalProfile* terminal_app_get_profile_by_name (TerminalApp *app, + const char *name); + +TerminalProfile* terminal_app_get_profile_by_visible_name (TerminalApp *app, + const char *name); + +/* may return NULL */ +TerminalProfile* terminal_app_get_default_profile (TerminalApp *app); + +/* never returns NULL if any profiles exist, one is always supposed to */ +TerminalProfile* terminal_app_get_profile_for_new_term (TerminalApp *app); + +TerminalEncoding *terminal_app_ensure_encoding (TerminalApp *app, + const char *charset); + +GHashTable *terminal_app_get_encodings (TerminalApp *app); + +GSList* terminal_app_get_active_encodings (TerminalApp *app); + +void terminal_app_save_config (TerminalApp *app, + GKeyFile *key_file); + +gboolean terminal_app_save_config_file (TerminalApp *app, + const char *file_name, + GError **error); + +G_END_DECLS + +#endif /* !TERMINAL_APP_H */ diff --git a/src/terminal-debug.c b/src/terminal-debug.c new file mode 100644 index 0000000..ec9c3ee --- /dev/null +++ b/src/terminal-debug.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002,2003 Red Hat, Inc. + * + * This is free software; you can redistribute it and/or modify it under + * the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 3 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 Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <config.h> + +#include <glib.h> + +#include "terminal-debug.h" + +TerminalDebugFlags _terminal_debug_flags; + +void +_terminal_debug_init(void) +{ +#ifdef MATE_ENABLE_DEBUG + const GDebugKey keys[] = { + { "accels", TERMINAL_DEBUG_ACCELS }, + { "encodings", TERMINAL_DEBUG_ENCODINGS }, + { "factory", TERMINAL_DEBUG_FACTORY }, + { "geometry", TERMINAL_DEBUG_GEOMETRY }, + { "mdi", TERMINAL_DEBUG_MDI }, + { "processes", TERMINAL_DEBUG_PROCESSES }, + { "profile", TERMINAL_DEBUG_PROFILE } + }; + + _terminal_debug_flags = g_parse_debug_string (g_getenv ("MATE_TERMINAL_DEBUG"), + keys, G_N_ELEMENTS (keys)); +#endif /* MATE_ENABLE_DEBUG */ +} + diff --git a/src/terminal-debug.h b/src/terminal-debug.h new file mode 100644 index 0000000..6be1716 --- /dev/null +++ b/src/terminal-debug.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2002 Red Hat, Inc. + * + * This is free software; you can redistribute it and/or modify it under + * the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 3 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 Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* The interfaces in this file are subject to change at any time. */ + +#ifndef MATE_ENABLE_DEBUG_H +#define MATE_ENABLE_DEBUG_H + +#include <glib.h> + +G_BEGIN_DECLS + +typedef enum { + TERMINAL_DEBUG_ACCELS = 1 << 0, + TERMINAL_DEBUG_ENCODINGS = 1 << 1, + TERMINAL_DEBUG_FACTORY = 1 << 2, + TERMINAL_DEBUG_GEOMETRY = 1 << 3, + TERMINAL_DEBUG_MDI = 1 << 4, + TERMINAL_DEBUG_PROCESSES = 1 << 5, + TERMINAL_DEBUG_PROFILE = 1 << 6 +} TerminalDebugFlags; + +void _terminal_debug_init(void); + +extern TerminalDebugFlags _terminal_debug_flags; +static inline gboolean _terminal_debug_on (TerminalDebugFlags flags) G_GNUC_CONST G_GNUC_UNUSED; + +static inline gboolean +_terminal_debug_on (TerminalDebugFlags flags) +{ + return (_terminal_debug_flags & flags) == flags; +} + +#ifdef MATE_ENABLE_DEBUG +#define _TERMINAL_DEBUG_IF(flags) if (G_UNLIKELY (_terminal_debug_on (flags))) +#else +#define _TERMINAL_DEBUG_IF(flags) if (0) +#endif + +#if defined(__GNUC__) && G_HAVE_GNUC_VARARGS +#define _terminal_debug_print(flags, fmt, ...) \ + G_STMT_START { _TERMINAL_DEBUG_IF(flags) g_printerr(fmt, ##__VA_ARGS__); } G_STMT_END +#else +#include <stdarg.h> +#include <glib/gstdio.h> +static void _terminal_debug_print (guint flags, const char *fmt, ...) +{ + if (_terminal_debug_on (flags)) { + va_list ap; + va_start (ap, fmt); + g_vfprintf (stderr, fmt, ap); + va_end (ap); + } +} +#endif + +G_END_DECLS + +#endif /* !MATE_ENABLE_DEBUG_H */ diff --git a/src/terminal-encoding.c b/src/terminal-encoding.c new file mode 100644 index 0000000..25d343b --- /dev/null +++ b/src/terminal-encoding.c @@ -0,0 +1,614 @@ +/* + * Copyright © 2002 Red Hat, Inc. + * Copyright © 2008 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> + +#include <gtk/gtk.h> + +#include "terminal-app.h" +#include "terminal-debug.h" +#include "terminal-encoding.h" +#include "terminal-intl.h" +#include "terminal-profile.h" +#include "terminal-util.h" + +/* Overview + * + * There's a list of character sets stored in mateconf, indicating + * which encodings to display in the encoding menu. + * + * We have a pre-canned list of available encodings + * (hardcoded in the table below) that can be added to + * the encoding menu, and to give a human-readable name + * to certain encodings. + * + * If the mateconf list contains an encoding not in the + * predetermined table, then that encoding is + * labeled "user defined" but still appears in the menu. + */ + +static const struct { + const char *charset; + const char *name; +} encodings[] = { + { "ISO-8859-1", N_("Western") }, + { "ISO-8859-2", N_("Central European") }, + { "ISO-8859-3", N_("South European") }, + { "ISO-8859-4", N_("Baltic") }, + { "ISO-8859-5", N_("Cyrillic") }, + { "ISO-8859-6", N_("Arabic") }, + { "ISO-8859-7", N_("Greek") }, + { "ISO-8859-8", N_("Hebrew Visual") }, + { "ISO-8859-8-I", N_("Hebrew") }, + { "ISO-8859-9", N_("Turkish") }, + { "ISO-8859-10", N_("Nordic") }, + { "ISO-8859-13", N_("Baltic") }, + { "ISO-8859-14", N_("Celtic") }, + { "ISO-8859-15", N_("Western") }, + { "ISO-8859-16", N_("Romanian") }, + { "UTF-8", N_("Unicode") }, + { "ARMSCII-8", N_("Armenian") }, + { "BIG5", N_("Chinese Traditional") }, + { "BIG5-HKSCS", N_("Chinese Traditional") }, + { "CP866", N_("Cyrillic/Russian") }, + { "EUC-JP", N_("Japanese") }, + { "EUC-KR", N_("Korean") }, + { "EUC-TW", N_("Chinese Traditional") }, + { "GB18030", N_("Chinese Simplified") }, + { "GB2312", N_("Chinese Simplified") }, + { "GBK", N_("Chinese Simplified") }, + { "GEORGIAN-PS", N_("Georgian") }, + { "IBM850", N_("Western") }, + { "IBM852", N_("Central European") }, + { "IBM855", N_("Cyrillic") }, + { "IBM857", N_("Turkish") }, + { "IBM862", N_("Hebrew") }, + { "IBM864", N_("Arabic") }, + { "ISO-2022-JP", N_("Japanese") }, + { "ISO-2022-KR", N_("Korean") }, + { "ISO-IR-111", N_("Cyrillic") }, + { "KOI8-R", N_("Cyrillic") }, + { "KOI8-U", N_("Cyrillic/Ukrainian") }, + { "MAC_ARABIC", N_("Arabic") }, + { "MAC_CE", N_("Central European") }, + { "MAC_CROATIAN", N_("Croatian") }, + { "MAC-CYRILLIC", N_("Cyrillic") }, + { "MAC_DEVANAGARI", N_("Hindi") }, + { "MAC_FARSI", N_("Persian") }, + { "MAC_GREEK", N_("Greek") }, + { "MAC_GUJARATI", N_("Gujarati") }, + { "MAC_GURMUKHI", N_("Gurmukhi") }, + { "MAC_HEBREW", N_("Hebrew") }, + { "MAC_ICELANDIC", N_("Icelandic") }, + { "MAC_ROMAN", N_("Western") }, + { "MAC_ROMANIAN", N_("Romanian") }, + { "MAC_TURKISH", N_("Turkish") }, + { "MAC_UKRAINIAN", N_("Cyrillic/Ukrainian") }, + { "SHIFT_JIS", N_("Japanese") }, + { "TCVN", N_("Vietnamese") }, + { "TIS-620", N_("Thai") }, + { "UHC", N_("Korean") }, + { "VISCII", N_("Vietnamese") }, + { "WINDOWS-1250", N_("Central European") }, + { "WINDOWS-1251", N_("Cyrillic") }, + { "WINDOWS-1252", N_("Western") }, + { "WINDOWS-1253", N_("Greek") }, + { "WINDOWS-1254", N_("Turkish") }, + { "WINDOWS-1255", N_("Hebrew") }, + { "WINDOWS-1256", N_("Arabic") }, + { "WINDOWS-1257", N_("Baltic") }, + { "WINDOWS-1258", N_("Vietnamese") }, +#if 0 + /* These encodings do NOT pass-through ASCII, so are always rejected. + * FIXME: why are they in this table; or rather why do we need + * the ASCII pass-through requirement? + */ + { "UTF-7", N_("Unicode") }, + { "UTF-16", N_("Unicode") }, + { "UCS-2", N_("Unicode") }, + { "UCS-4", N_("Unicode") }, + { "JOHAB", N_("Korean") }, +#endif +}; + +typedef struct { + GtkWidget *dialog; + GtkListStore *base_store; + GtkTreeView *available_tree_view; + GtkTreeSelection *available_selection; + GtkTreeModel *available_model; + GtkTreeView *active_tree_view; + GtkTreeSelection *active_selection; + GtkTreeModel *active_model; + GtkWidget *add_button; + GtkWidget *remove_button; +} EncodingDialogData; + +static GtkWidget *encoding_dialog = NULL; + +TerminalEncoding * +terminal_encoding_new (const char *charset, + const char *display_name, + gboolean is_custom, + gboolean force_valid) +{ + TerminalEncoding *encoding; + + encoding = g_slice_new (TerminalEncoding); + encoding->refcount = 1; + encoding->id = g_strdup (charset); + encoding->name = g_strdup (display_name); + encoding->valid = encoding->validity_checked = force_valid; + encoding->is_custom = is_custom; + encoding->is_active = FALSE; + + return encoding; +} + +TerminalEncoding* +terminal_encoding_ref (TerminalEncoding *encoding) +{ + g_return_val_if_fail (encoding != NULL, NULL); + + encoding->refcount++; + return encoding; +} + +void +terminal_encoding_unref (TerminalEncoding *encoding) +{ + if (--encoding->refcount > 0) + return; + + g_free (encoding->name); + g_free (encoding->id); + g_slice_free (TerminalEncoding, encoding); +} + +const char * +terminal_encoding_get_id (TerminalEncoding *encoding) +{ + g_return_val_if_fail (encoding != NULL, NULL); + + return encoding->id; +} + +const char * +terminal_encoding_get_charset (TerminalEncoding *encoding) +{ + g_return_val_if_fail (encoding != NULL, NULL); + + if (strcmp (encoding->id, "current") == 0) + { + const char *charset; + + g_get_charset (&charset); + return charset; + } + + return encoding->id; +} + +gboolean +terminal_encoding_is_valid (TerminalEncoding *encoding) +{ + /* All of the printing ASCII characters from space (32) to the tilde (126) */ + static const char ascii_sample[] = + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + char *converted; + gsize bytes_read = 0, bytes_written = 0; + GError *error = NULL; + + if (encoding->validity_checked) + return encoding->valid; + + /* Test that the encoding is a proper superset of ASCII (which naive + * apps are going to use anyway) by attempting to validate the text + * using the current encoding. This also flushes out any encodings + * which the underlying GIConv implementation can't support. + */ + converted = g_convert (ascii_sample, sizeof (ascii_sample) - 1, + terminal_encoding_get_charset (encoding), "UTF-8", + &bytes_read, &bytes_written, &error); + + /* The encoding is only valid if ASCII passes through cleanly. */ + encoding->valid = (bytes_read == (sizeof (ascii_sample) - 1)) && + (converted != NULL) && + (strcmp (converted, ascii_sample) == 0); + +#ifdef MATE_ENABLE_DEBUG + _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_ENCODINGS) + { + if (!encoding->valid) + { + _terminal_debug_print (TERMINAL_DEBUG_ENCODINGS, + "Rejecting encoding %s as invalid:\n", + terminal_encoding_get_charset (encoding)); + _terminal_debug_print (TERMINAL_DEBUG_ENCODINGS, + " input \"%s\"\n", + ascii_sample); + _terminal_debug_print (TERMINAL_DEBUG_ENCODINGS, + " output \"%s\" bytes read %u written %u\n", + converted ? converted : "(null)", bytes_read, bytes_written); + if (error) + _terminal_debug_print (TERMINAL_DEBUG_ENCODINGS, + " Error: %s\n", + error->message); + } + else + _terminal_debug_print (TERMINAL_DEBUG_ENCODINGS, + "Encoding %s is valid\n\n", + terminal_encoding_get_charset (encoding)); + } +#endif + + g_clear_error (&error); + g_free (converted); + + encoding->validity_checked = TRUE; + return encoding->valid; +} + +GType +terminal_encoding_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + type = g_boxed_type_register_static (I_("TerminalEncoding"), + (GBoxedCopyFunc) terminal_encoding_ref, + (GBoxedFreeFunc) terminal_encoding_unref); + } + + return type; +} + +static void +update_active_encodings_mateconf (void) +{ + GSList *list, *l; + GSList *strings = NULL; + MateConfClient *conf; + + list = terminal_app_get_active_encodings (terminal_app_get ()); + for (l = list; l != NULL; l = l->next) + { + TerminalEncoding *encoding = (TerminalEncoding *) l->data; + + strings = g_slist_prepend (strings, (gpointer) terminal_encoding_get_id (encoding)); + } + + conf = mateconf_client_get_default (); + mateconf_client_set_list (conf, + CONF_GLOBAL_PREFIX"/active_encodings", + MATECONF_VALUE_STRING, + strings, + NULL); + g_object_unref (conf); + + g_slist_free (strings); + g_slist_foreach (list, (GFunc) terminal_encoding_unref, NULL); + g_slist_free (list); +} + +static void +response_callback (GtkWidget *window, + int id, + EncodingDialogData *data) +{ + if (id == GTK_RESPONSE_HELP) + terminal_util_show_help ("mate-terminal-encoding-add", GTK_WINDOW (window)); + else + gtk_widget_destroy (GTK_WIDGET (window)); +} + +enum +{ + COLUMN_NAME, + COLUMN_CHARSET, + COLUMN_DATA, + N_COLUMNS +}; + +static void +selection_changed_cb (GtkTreeSelection *selection, + EncodingDialogData *data) +{ + GtkWidget *button; + gboolean have_selection; + + if (selection == data->available_selection) + button = data->add_button; + else if (selection == data->active_selection) + button = data->remove_button; + else + g_assert_not_reached (); + + have_selection = gtk_tree_selection_get_selected (selection, NULL, NULL); + gtk_widget_set_sensitive (button, have_selection); +} + +static void +button_clicked_cb (GtkWidget *button, + EncodingDialogData *data) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter filter_iter, iter; + TerminalEncoding *encoding; + + if (button == data->add_button) + selection = data->available_selection; + else if (button == data->remove_button) + selection = data->active_selection; + else + g_assert_not_reached (); + + if (!gtk_tree_selection_get_selected (selection, &model, &filter_iter)) + return; + + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), + &iter, + &filter_iter); + + model = GTK_TREE_MODEL (data->base_store); + gtk_tree_model_get (model, &iter, COLUMN_DATA, &encoding, -1); + g_assert (encoding != NULL); + + if (button == data->add_button) + encoding->is_active = TRUE; + else if (button == data->remove_button) + encoding->is_active = FALSE; + else + g_assert_not_reached (); + + terminal_encoding_unref (encoding); + + /* We don't need to emit row-changed here, since updating the mateconf pref + * will update the models. + */ + update_active_encodings_mateconf (); +} + +static void +liststore_insert_encoding (gpointer key, + TerminalEncoding *encoding, + GtkListStore *store) +{ + GtkTreeIter iter; + + if (!terminal_encoding_is_valid (encoding)) + return; + + gtk_list_store_insert_with_values (store, &iter, -1, + COLUMN_CHARSET, terminal_encoding_get_charset (encoding), + COLUMN_NAME, encoding->name, + COLUMN_DATA, encoding, + -1); +} + +static gboolean +filter_active_encodings (GtkTreeModel *child_model, + GtkTreeIter *child_iter, + gpointer data) +{ + TerminalEncoding *encoding; + gboolean active = GPOINTER_TO_UINT (data); + gboolean visible; + + gtk_tree_model_get (child_model, child_iter, COLUMN_DATA, &encoding, -1); + visible = active ? encoding->is_active : !encoding->is_active; + terminal_encoding_unref (encoding); + + return visible; +} + +static GtkTreeModel * +encodings_create_treemodel (GtkListStore *base_store, + gboolean active) +{ + GtkTreeModel *model; + + model = gtk_tree_model_filter_new (GTK_TREE_MODEL (base_store), NULL); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (model), + filter_active_encodings, + GUINT_TO_POINTER (active), NULL); + + return model; +} + +static void +encodings_list_changed_cb (TerminalApp *app, + EncodingDialogData *data) +{ + gtk_list_store_clear (data->base_store); + + g_hash_table_foreach (terminal_app_get_encodings (app), (GHFunc) liststore_insert_encoding, data->base_store); +} + +static void +encoding_dialog_data_free (EncodingDialogData *data) +{ + g_signal_handlers_disconnect_by_func (terminal_app_get (), + G_CALLBACK (encodings_list_changed_cb), + data); + + g_free (data); +} + +void +terminal_encoding_dialog_show (GtkWindow *transient_parent) +{ + TerminalApp *app; + GtkCellRenderer *cell_renderer; + GtkTreeViewColumn *column; + GtkTreeModel *model; + EncodingDialogData *data; + + if (encoding_dialog) + { + gtk_window_set_transient_for (GTK_WINDOW (encoding_dialog), transient_parent); + gtk_window_present (GTK_WINDOW (encoding_dialog)); + return; + } + + data = g_new (EncodingDialogData, 1); + + if (!terminal_util_load_builder_file ("encodings-dialog.ui", + "encodings-dialog", &data->dialog, + "add-button", &data->add_button, + "remove-button", &data->remove_button, + "available-treeview", &data->available_tree_view, + "displayed-treeview", &data->active_tree_view, + NULL)) + { + g_free (data); + return; + } + + g_object_set_data_full (G_OBJECT (data->dialog), "GT::Data", data, (GDestroyNotify) encoding_dialog_data_free); + + gtk_window_set_transient_for (GTK_WINDOW (data->dialog), transient_parent); + gtk_window_set_role (GTK_WINDOW (data->dialog), "mate-terminal-encodings"); + g_signal_connect (data->dialog, "response", + G_CALLBACK (response_callback), data); + + /* buttons */ + g_signal_connect (data->add_button, "clicked", + G_CALLBACK (button_clicked_cb), data); + + g_signal_connect (data->remove_button, "clicked", + G_CALLBACK (button_clicked_cb), data); + + /* Tree view of available encodings */ + /* Column 1 */ + cell_renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("_Description"), + cell_renderer, + "text", COLUMN_NAME, + NULL); + gtk_tree_view_append_column (data->available_tree_view, column); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME); + + /* Column 2 */ + cell_renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("_Encoding"), + cell_renderer, + "text", COLUMN_CHARSET, + NULL); + gtk_tree_view_append_column (data->available_tree_view, column); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_CHARSET); + + data->available_selection = gtk_tree_view_get_selection (data->available_tree_view); + gtk_tree_selection_set_mode (data->available_selection, GTK_SELECTION_BROWSE); + + g_signal_connect (data->available_selection, "changed", + G_CALLBACK (selection_changed_cb), data); + + /* Tree view of selected encodings */ + /* Column 1 */ + cell_renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("_Description"), + cell_renderer, + "text", COLUMN_NAME, + NULL); + gtk_tree_view_append_column (data->active_tree_view, column); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME); + + /* Column 2 */ + cell_renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("_Encoding"), + cell_renderer, + "text", COLUMN_CHARSET, + NULL); + gtk_tree_view_append_column (data->active_tree_view, column); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_CHARSET); + + /* Add the data */ + + data->active_selection = gtk_tree_view_get_selection (data->active_tree_view); + gtk_tree_selection_set_mode (data->active_selection, GTK_SELECTION_BROWSE); + + g_signal_connect (data->active_selection, "changed", + G_CALLBACK (selection_changed_cb), data); + + data->base_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, TERMINAL_TYPE_ENCODING); + + app = terminal_app_get (); + encodings_list_changed_cb (app, data); + g_signal_connect (app, "encoding-list-changed", + G_CALLBACK (encodings_list_changed_cb), data); + + /* Now turn on sorting */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->base_store), + COLUMN_NAME, + GTK_SORT_ASCENDING); + + model = encodings_create_treemodel (data->base_store, FALSE); + gtk_tree_view_set_model (data->available_tree_view, model); + g_object_unref (model); + + model = encodings_create_treemodel (data->base_store, TRUE); + gtk_tree_view_set_model (data->active_tree_view, model); + g_object_unref (model); + + g_object_unref (data->base_store); + + gtk_window_present (GTK_WINDOW (data->dialog)); + + encoding_dialog = data->dialog; + g_signal_connect (data->dialog, "destroy", + G_CALLBACK (gtk_widget_destroyed), &encoding_dialog); +} + +GHashTable * +terminal_encodings_get_builtins (void) +{ + GHashTable *encodings_hashtable; + guint i; + TerminalEncoding *encoding; + + encodings_hashtable = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, + (GDestroyNotify) terminal_encoding_unref); + + + /* Placeholder entry for the current locale's charset */ + encoding = terminal_encoding_new ("current", + _("Current Locale"), + FALSE, + TRUE); + g_hash_table_insert (encodings_hashtable, + (gpointer) terminal_encoding_get_id (encoding), + encoding); + + for (i = 0; i < G_N_ELEMENTS (encodings); ++i) + { + encoding = terminal_encoding_new (encodings[i].charset, + _(encodings[i].name), + FALSE, + FALSE); + g_hash_table_insert (encodings_hashtable, + (gpointer) terminal_encoding_get_id (encoding), + encoding); + } + + return encodings_hashtable; +} diff --git a/src/terminal-encoding.h b/src/terminal-encoding.h new file mode 100644 index 0000000..ddfe9ed --- /dev/null +++ b/src/terminal-encoding.h @@ -0,0 +1,61 @@ +/* Encoding stuff */ + +/* + * Copyright © 2002 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TERMINAL_ENCODING_H +#define TERMINAL_ENCODING_H + +#include <gtk/gtk.h> + +#define TERMINAL_TYPE_ENCODING (terminal_encoding_get_type ()) + +typedef struct +{ + int refcount; + char *id; + char *name; + guint valid : 1; + guint validity_checked : 1; + guint is_custom : 1; + guint is_active : 1; +} TerminalEncoding; + +GType terminal_encoding_get_type (void); + +TerminalEncoding *terminal_encoding_new (const char *charset, + const char *display_name, + gboolean is_custom, + gboolean force_valid); + +TerminalEncoding *terminal_encoding_ref (TerminalEncoding *encoding); + +void terminal_encoding_unref (TerminalEncoding *encoding); + +gboolean terminal_encoding_is_valid (TerminalEncoding *encoding); + +const char *terminal_encoding_get_id (TerminalEncoding *encoding); + +const char *terminal_encoding_get_charset (TerminalEncoding *encoding); + +GHashTable *terminal_encodings_get_builtins (void); + +void terminal_encoding_dialog_show (GtkWindow *transient_parent); + +#endif /* TERMINAL_ENCODING_H */ diff --git a/src/terminal-info-bar.c b/src/terminal-info-bar.c new file mode 100644 index 0000000..57c132b --- /dev/null +++ b/src/terminal-info-bar.c @@ -0,0 +1,119 @@ +/* + * Copyright © 2010 Christian Persch + * + * 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 3, or (at your option) + * any later version. + * + * This program is distributed in the hope info_bar 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 "terminal-info-bar.h" + +#include <gtk/gtk.h> + +#define TERMINAL_INFO_BAR_GET_PRIVATE(info_bar)(G_TYPE_INSTANCE_GET_PRIVATE ((info_bar), TERMINAL_TYPE_INFO_BAR, TerminalInfoBarPrivate)) + +struct _TerminalInfoBarPrivate +{ + GtkWidget *content_box; +}; + +G_DEFINE_TYPE (TerminalInfoBar, terminal_info_bar, GTK_TYPE_INFO_BAR) + +/* helper functions */ + +static void +terminal_info_bar_init (TerminalInfoBar *bar) +{ + GtkInfoBar *info_bar = GTK_INFO_BAR (bar); + TerminalInfoBarPrivate *priv; + + priv = bar->priv = TERMINAL_INFO_BAR_GET_PRIVATE (bar); + + priv->content_box = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (info_bar)), + priv->content_box, TRUE, TRUE, 0); +} + +static void +terminal_info_bar_class_init (TerminalInfoBarClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (gobject_class, sizeof (TerminalInfoBarPrivate)); +} + +/* public API */ + +/** + * terminal_info_bar_new: + * @type: a #GtkMessageType + * + * Returns: a new #TerminalInfoBar for @screen + */ +GtkWidget * +terminal_info_bar_new (GtkMessageType type, + const char *first_button_text, + ...) +{ + GtkWidget *info_bar; + va_list args; + + info_bar = g_object_new (TERMINAL_TYPE_INFO_BAR, + "message-type", type, + NULL); + + va_start (args, first_button_text); + while (first_button_text != NULL) { + int response_id; + + response_id = va_arg (args, int); + gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), + first_button_text, response_id); + + first_button_text = va_arg (args, const char *); + } + va_end (args); + + return info_bar; +} + +void +terminal_info_bar_format_text (TerminalInfoBar *bar, + const char *format, + ...) +{ + TerminalInfoBarPrivate *priv; + char *text; + GtkWidget *label; + va_list args; + + g_return_if_fail (TERMINAL_IS_INFO_BAR (bar)); + + priv = bar->priv; + + va_start (args, format); + text = g_strdup_vprintf (format, args); + va_end (args); + + label = gtk_label_new (text); + g_free (text); + + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_selectable (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0); + + gtk_box_pack_start (GTK_BOX (priv->content_box), label, FALSE, FALSE, 0); + gtk_widget_show_all (priv->content_box); +} diff --git a/src/terminal-info-bar.h b/src/terminal-info-bar.h new file mode 100644 index 0000000..09d2794 --- /dev/null +++ b/src/terminal-info-bar.h @@ -0,0 +1,62 @@ +/* + * Copyright © 2010 Christian Persch + * + * 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 3, or (at your option) + * any later version. + * + * This program is distributed in the hope info_bar 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 TERMINAL_INFO_BAR_H +#define TERMINAL_INFO_BAR_H + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define TERMINAL_TYPE_INFO_BAR (terminal_info_bar_get_type ()) +#define TERMINAL_INFO_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TERMINAL_TYPE_INFO_BAR, TerminalInfoBar)) +#define TERMINAL_INFO_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TERMINAL_TYPE_INFO_BAR, TerminalInfoBarClass)) +#define TERMINAL_IS_INFO_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TERMINAL_TYPE_INFO_BAR)) +#define TERMINAL_IS_INFO_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TERMINAL_TYPE_INFO_BAR)) +#define TERMINAL_INFO_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TERMINAL_TYPE_INFO_BAR, TerminalInfoBarClass)) + +typedef struct _TerminalInfoBar TerminalInfoBar; +typedef struct _TerminalInfoBarClass TerminalInfoBarClass; +typedef struct _TerminalInfoBarPrivate TerminalInfoBarPrivate; + +struct _TerminalInfoBar +{ + GtkInfoBar parent_instance; + + /*< private >*/ + TerminalInfoBarPrivate *priv; +}; + +struct _TerminalInfoBarClass +{ + GtkInfoBarClass parent_class; +}; + +GType terminal_info_bar_get_type (void); + +GtkWidget *terminal_info_bar_new (GtkMessageType type, + const char *first_button_text, + ...) G_GNUC_NULL_TERMINATED; + +void terminal_info_bar_format_text (TerminalInfoBar *bar, + const char *format, + ...) G_GNUC_PRINTF (2, 3); + +G_END_DECLS + +#endif /* !TERMINAL_INFO_BAR_H */ diff --git a/src/terminal-intl.h b/src/terminal-intl.h new file mode 100644 index 0000000..cc9f9a2 --- /dev/null +++ b/src/terminal-intl.h @@ -0,0 +1,27 @@ +/* + * Copyright © 2002 Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TERMINAL_INTL_H +#define TERMINAL_INTL_H + +#include <glib/gi18n.h> + +#define I_(string) g_intern_static_string (string) + +#endif /* TERMINAL_INTL_H */ diff --git a/src/terminal-marshal.list b/src/terminal-marshal.list new file mode 100644 index 0000000..3e91f60 --- /dev/null +++ b/src/terminal-marshal.list @@ -0,0 +1 @@ +BOOLEAN:STRING,INT,UINT diff --git a/src/terminal-options.c b/src/terminal-options.c new file mode 100644 index 0000000..82a0d7c --- /dev/null +++ b/src/terminal-options.c @@ -0,0 +1,1396 @@ +/* + * Copyright © 2001, 2002 Havoc Pennington + * Copyright © 2002 Red Hat, Inc. + * Copyright © 2002 Sun Microsystems + * Copyright © 2003 Mariano Suarez-Alvarez + * Copyright © 2008 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#include <glib.h> + +#include "terminal-options.h" +#include "terminal-screen.h" +#include "terminal-app.h" +#include "terminal-intl.h" +#include "terminal-util.h" + +static GOptionContext *get_goption_context (TerminalOptions *options); + +static InitialTab* +initial_tab_new (const char *profile, + gboolean is_id) +{ + InitialTab *it; + + it = g_slice_new (InitialTab); + + it->profile = g_strdup (profile); + it->profile_is_id = is_id; + it->exec_argv = NULL; + it->title = NULL; + it->working_dir = NULL; + it->zoom = 1.0; + it->zoom_set = FALSE; + it->active = FALSE; + + return it; +} + +static void +initial_tab_free (InitialTab *it) +{ + g_free (it->profile); + g_strfreev (it->exec_argv); + g_free (it->title); + g_free (it->working_dir); + g_slice_free (InitialTab, it); +} + +static InitialWindow* +initial_window_new (guint source_tag) +{ + InitialWindow *iw; + + iw = g_slice_new0 (InitialWindow); + iw->source_tag = source_tag; + + return iw; +} + +static void +initial_window_free (InitialWindow *iw) +{ + g_list_foreach (iw->tabs, (GFunc) initial_tab_free, NULL); + g_list_free (iw->tabs); + g_free (iw->geometry); + g_free (iw->role); + g_slice_free (InitialWindow, iw); +} + +static void +apply_defaults (TerminalOptions *options, + InitialWindow *iw) +{ + if (options->default_role) + { + iw->role = options->default_role; + options->default_role = NULL; + } + + if (iw->geometry == NULL) + iw->geometry = g_strdup (options->default_geometry); + + if (options->default_window_menubar_forced) + { + iw->force_menubar_state = TRUE; + iw->menubar_state = options->default_window_menubar_state; + + options->default_window_menubar_forced = FALSE; + } + + iw->start_fullscreen |= options->default_fullscreen; + iw->start_maximized |= options->default_maximize; +} + +static InitialWindow* +ensure_top_window (TerminalOptions *options) +{ + InitialWindow *iw; + + if (options->initial_windows == NULL) + { + iw = initial_window_new (0); + iw->tabs = g_list_append (NULL, initial_tab_new (NULL, FALSE)); + apply_defaults (options, iw); + + options->initial_windows = g_list_append (options->initial_windows, iw); + } + else + { + iw = g_list_last (options->initial_windows)->data; + } + + g_assert (iw->tabs); + + return iw; +} + +static InitialTab* +ensure_top_tab (TerminalOptions *options) +{ + InitialWindow *iw; + InitialTab *it; + + iw = ensure_top_window (options); + + g_assert (iw->tabs); + + it = g_list_last (iw->tabs)->data; + + return it; +} + +static InitialWindow* +add_new_window (TerminalOptions *options, + const char *profile, + gboolean is_id) +{ + InitialWindow *iw; + + iw = initial_window_new (0); + iw->tabs = g_list_prepend (NULL, initial_tab_new (profile, is_id)); + apply_defaults (options, iw); + + options->initial_windows = g_list_append (options->initial_windows, iw); + + return iw; +} + +/* handle deprecated command line options */ +static gboolean +unsupported_option_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + g_printerr (_("Option \"%s\" is no longer supported in this version of mate-terminal;" + " you might want to create a profile with the desired setting, and use" + " the new '--profile' option\n"), option_name); + return TRUE; /* we do not want to bail out here but continue */ +} + + +static gboolean G_GNUC_NORETURN +option_version_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + g_print ("%s %s\n", _("MATE Terminal"), VERSION); + + exit (EXIT_SUCCESS); +} + +static gboolean +option_command_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + GError *err = NULL; + char **exec_argv; + + if (!g_shell_parse_argv (value, NULL, &exec_argv, &err)) + { + g_set_error(error, + G_OPTION_ERROR, + G_OPTION_ERROR_BAD_VALUE, + _("Argument to \"%s\" is not a valid command: %s"), + "--command/-e", + err->message); + g_error_free (err); + return FALSE; + } + + if (options->initial_windows) + { + InitialTab *it = ensure_top_tab (options); + + g_strfreev (it->exec_argv); + it->exec_argv = exec_argv; + } + else + { + g_strfreev (options->exec_argv); + options->exec_argv = exec_argv; + } + + return TRUE; +} + +static gboolean +option_profile_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + + if (options->initial_windows) + { + InitialTab *it = ensure_top_tab (options); + + g_free (it->profile); + it->profile = g_strdup (value); + it->profile_is_id = FALSE; + } + else + { + g_free (options->default_profile); + options->default_profile = g_strdup (value); + options->default_profile_is_id = FALSE; + } + + return TRUE; +} + +static gboolean +option_profile_id_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + + if (options->initial_windows) + { + InitialTab *it = ensure_top_tab (options); + + g_free (it->profile); + it->profile = g_strdup (value); + it->profile_is_id = TRUE; + } + else + { + g_free (options->default_profile); + options->default_profile = g_strdup (value); + options->default_profile_is_id = TRUE; + } + + return TRUE; +} + + +static gboolean +option_window_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + gboolean is_profile_id; + + is_profile_id = g_str_has_suffix (option_name, "-with-profile-internal-id"); + + add_new_window (options, value, is_profile_id); + + return TRUE; +} + +static gboolean +option_tab_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + gboolean is_profile_id; + + is_profile_id = g_str_has_suffix (option_name, "-with-profile-internal-id"); + + if (options->initial_windows) + { + InitialWindow *iw; + + iw = g_list_last (options->initial_windows)->data; + iw->tabs = g_list_append (iw->tabs, initial_tab_new (value, is_profile_id)); + } + else + add_new_window (options, value, is_profile_id); + + return TRUE; +} + +static gboolean +option_role_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + InitialWindow *iw; + + if (options->initial_windows) + { + iw = g_list_last (options->initial_windows)->data; + iw->role = g_strdup (value); + } + else if (!options->default_role) + options->default_role = g_strdup (value); + else + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "%s", _("Two roles given for one window")); + return FALSE; + } + + return TRUE; +} + +static gboolean +option_show_menubar_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + InitialWindow *iw; + + if (options->initial_windows) + { + iw = g_list_last (options->initial_windows)->data; + if (iw->force_menubar_state && iw->menubar_state == TRUE) + { + g_printerr (_("\"%s\" option given twice for the same window\n"), + "--show-menubar"); + + return TRUE; + } + + iw->force_menubar_state = TRUE; + iw->menubar_state = TRUE; + } + else + { + options->default_window_menubar_forced = TRUE; + options->default_window_menubar_state = TRUE; + } + + return TRUE; +} + +static gboolean +option_hide_menubar_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + InitialWindow *iw; + + if (options->initial_windows) + { + iw = g_list_last (options->initial_windows)->data; + + if (iw->force_menubar_state && iw->menubar_state == FALSE) + { + g_printerr (_("\"%s\" option given twice for the same window\n"), + "--hide-menubar"); + return TRUE; + } + + iw->force_menubar_state = TRUE; + iw->menubar_state = FALSE; + } + else + { + options->default_window_menubar_forced = TRUE; + options->default_window_menubar_state = FALSE; + } + + return TRUE; +} + +static gboolean +option_maximize_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + InitialWindow *iw; + + if (options->initial_windows) + { + iw = g_list_last (options->initial_windows)->data; + iw->start_maximized = TRUE; + } + else + options->default_maximize = TRUE; + + return TRUE; +} + +static gboolean +option_fullscreen_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + + if (options->initial_windows) + { + InitialWindow *iw; + + iw = g_list_last (options->initial_windows)->data; + iw->start_fullscreen = TRUE; + } + else + options->default_fullscreen = TRUE; + + return TRUE; +} + +static gboolean +option_geometry_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + + if (options->initial_windows) + { + InitialWindow *iw; + + iw = g_list_last (options->initial_windows)->data; + iw->geometry = g_strdup (value); + } + else + options->default_geometry = g_strdup (value); + + return TRUE; +} + +static gboolean +option_disable_factory_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + + options->use_factory = FALSE; + + return TRUE; +} + +static gboolean +option_load_save_config_cb (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + + if (options->config_file) + { + g_set_error_literal (error, TERMINAL_OPTION_ERROR, TERMINAL_OPTION_ERROR_EXCLUSIVE_OPTIONS, + "Options \"--load-config\" and \"--save-config\" are mutually exclusive"); + return FALSE; + } + + options->config_file = terminal_util_resolve_relative_path (options->default_working_dir, value); + options->load_config = strcmp (option_name, "--load-config") == 0; + options->save_config = strcmp (option_name, "--save-config") == 0; + + return TRUE; +} + +static gboolean +option_title_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + + if (options->initial_windows) + { + InitialTab *it = ensure_top_tab (options); + + g_free (it->title); + it->title = g_strdup (value); + } + else + { + g_free (options->default_title); + options->default_title = g_strdup (value); + } + + return TRUE; +} + +static gboolean +option_working_directory_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + + if (options->initial_windows) + { + InitialTab *it = ensure_top_tab (options); + + g_free (it->working_dir); + it->working_dir = g_strdup (value); + } + else + { + g_free (options->default_working_dir); + options->default_working_dir = g_strdup (value); + } + + return TRUE; +} + +static gboolean +option_active_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + InitialTab *it; + + it = ensure_top_tab (options); + it->active = TRUE; + + return TRUE; +} + +static gboolean +option_zoom_callback (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + double zoom; + char *end; + + /* Try reading a locale-style double first, in case it was + * typed by a person, then fall back to ascii_strtod (we + * always save session in C locale format) + */ + end = NULL; + errno = 0; + zoom = g_strtod (value, &end); + if (end == NULL || *end != '\0') + { + g_set_error (error, + G_OPTION_ERROR, + G_OPTION_ERROR_BAD_VALUE, + _("\"%s\" is not a valid zoom factor"), + value); + return FALSE; + } + + if (zoom < (TERMINAL_SCALE_MINIMUM + 1e-6)) + { + g_printerr (_("Zoom factor \"%g\" is too small, using %g\n"), + zoom, + TERMINAL_SCALE_MINIMUM); + zoom = TERMINAL_SCALE_MINIMUM; + } + + if (zoom > (TERMINAL_SCALE_MAXIMUM - 1e-6)) + { + g_printerr (_("Zoom factor \"%g\" is too large, using %g\n"), + zoom, + TERMINAL_SCALE_MAXIMUM); + zoom = TERMINAL_SCALE_MAXIMUM; + } + + if (options->initial_windows) + { + InitialTab *it = ensure_top_tab (options); + it->zoom = zoom; + it->zoom_set = TRUE; + } + else + options->zoom = zoom; + + return TRUE; +} + +/* Evaluation of the arguments given to the command line options */ +static gboolean +digest_options_callback (GOptionContext *context, + GOptionGroup *group, + gpointer data, + GError **error) +{ + TerminalOptions *options = data; + InitialTab *it; + + if (options->execute) + { + if (options->exec_argv == NULL) + { + g_set_error (error, + G_OPTION_ERROR, + G_OPTION_ERROR_BAD_VALUE, + _("Option \"%s\" requires specifying the command to run" + " on the rest of the command line"), + "--execute/-x"); + return FALSE; + } + + /* Apply -x/--execute command only to the first tab */ + it = ensure_top_tab (options); + it->exec_argv = options->exec_argv; + options->exec_argv = NULL; + } + + return TRUE; +} + +/** + * terminal_options_parse: + * @working_directory: the default working directory + * @display_name: the default X display name + * @startup_id: the startup notification ID + * @env: the environment as variable=value pairs + * @remote_arguments: whether the caller is the factory process or not + * @ignore_unknown_options: whether to ignore unknown options when parsing + * the arguments + * @argcp: (inout) address of the argument count. Changed if any arguments were handled + * @argvp: (inout) address of the argument vector. Any parameters understood by + * the terminal #GOptionContext are removed + * @error: a #GError to fill in + * @...: a %NULL terminated list of extra #GOptionGroup<!-- -->s + * + * Parses the argument vector *@argvp. + * + * Returns: a new #TerminalOptions containing the windows and tabs to open, + * or %NULL on error. + */ +TerminalOptions * +terminal_options_parse (const char *working_directory, + const char *display_name, + const char *startup_id, + char **env, + gboolean remote_arguments, + gboolean ignore_unknown_options, + int *argcp, + char ***argvp, + GError **error, + ...) +{ + TerminalOptions *options; + GOptionContext *context; + GOptionGroup *extra_group; + va_list va_args; + gboolean retval; + int i; + char **argv = *argvp; + + options = g_slice_new0 (TerminalOptions); + + options->remote_arguments = remote_arguments; + options->default_window_menubar_forced = FALSE; + options->default_window_menubar_state = TRUE; + options->default_fullscreen = FALSE; + options->default_maximize = FALSE; + options->execute = FALSE; + options->use_factory = TRUE; + + options->env = g_strdupv (env); + options->startup_id = g_strdup (startup_id && startup_id[0] ? startup_id : NULL); + options->display_name = g_strdup (display_name); + options->initial_windows = NULL; + options->default_role = NULL; + options->default_geometry = NULL; + options->default_title = NULL; + options->zoom = 1.0; + + options->screen_number = -1; + options->default_working_dir = g_strdup (working_directory); + + /* The old -x/--execute option is broken, so we need to pre-scan for it. */ + /* We now also support passing the command after the -- switch. */ + options->exec_argv = NULL; + for (i = 1 ; i < *argcp; ++i) + { + gboolean is_execute; + gboolean is_dashdash; + int j, last; + + is_execute = strcmp (argv[i], "-x") == 0 || strcmp (argv[i], "--execute") == 0; + is_dashdash = strcmp (argv[i], "--") == 0; + + if (!is_execute && !is_dashdash) + continue; + + options->execute = is_execute; + + /* Skip the switch */ + last = i; + ++i; + if (i == *argcp) + break; /* we'll complain about this later for -x/--execute; it's fine for -- */ + + /* Collect the args, and remove them from argv */ + options->exec_argv = g_new0 (char*, *argcp - i + 1); + for (j = 0; i < *argcp; ++i, ++j) + options->exec_argv[j] = g_strdup (argv[i]); + options->exec_argv[j] = NULL; + + *argcp = last; + break; + } + + context = get_goption_context (options); + + g_option_context_set_ignore_unknown_options (context, ignore_unknown_options); + + va_start (va_args, error); + extra_group = va_arg (va_args, GOptionGroup*); + while (extra_group != NULL) + { + g_option_context_add_group (context, extra_group); + extra_group = va_arg (va_args, GOptionGroup*); + } + va_end (va_args); + + retval = g_option_context_parse (context, argcp, argvp, error); + g_option_context_free (context); + + if (retval) + return options; + + terminal_options_free (options); + return NULL; +} + +/** + * terminal_options_merge_config: + * @options: + * @key_file: a #GKeyFile containing to merge the options from + * @source_tag: a source_tag to use in new #InitialWindow<!-- -->s + * @error: a #GError to fill in + * + * Merges the saved options from @key_file into @options. + * + * Returns: %TRUE if @key_file was a valid key file containing a stored + * terminal configuration, or %FALSE on error + */ +gboolean +terminal_options_merge_config (TerminalOptions *options, + GKeyFile *key_file, + guint source_tag, + GError **error) +{ + int version, compat_version; + char **groups; + guint i; + gboolean have_error = FALSE; + GList *initial_windows = NULL; + + if (!g_key_file_has_group (key_file, TERMINAL_CONFIG_GROUP)) + { + g_set_error_literal (error, TERMINAL_OPTION_ERROR, + TERMINAL_OPTION_ERROR_INVALID_CONFIG_FILE, + _("Not a valid terminal config file.")); + return FALSE; + } + + version = g_key_file_get_integer (key_file, TERMINAL_CONFIG_GROUP, TERMINAL_CONFIG_PROP_VERSION, NULL); + compat_version = g_key_file_get_integer (key_file, TERMINAL_CONFIG_GROUP, TERMINAL_CONFIG_PROP_COMPAT_VERSION, NULL); + + if (version <= 0 || + compat_version <= 0 || + compat_version > TERMINAL_CONFIG_COMPAT_VERSION) + { + g_set_error_literal (error, TERMINAL_OPTION_ERROR, + TERMINAL_OPTION_ERROR_INCOMPATIBLE_CONFIG_FILE, + _("Incompatible terminal config file version.")); + return FALSE; + } + + groups = g_key_file_get_string_list (key_file, TERMINAL_CONFIG_GROUP, TERMINAL_CONFIG_PROP_WINDOWS, NULL, error); + if (!groups) + return FALSE; + + for (i = 0; groups[i]; ++i) + { + const char *window_group = groups[i]; + char **tab_groups; + InitialWindow *iw; + guint j; + + tab_groups = g_key_file_get_string_list (key_file, window_group, TERMINAL_CONFIG_WINDOW_PROP_TABS, NULL, error); + if (!tab_groups) + continue; /* no tabs in this window, skip it */ + + iw = initial_window_new (source_tag); + initial_windows = g_list_append (initial_windows, iw); + apply_defaults (options, iw); + + iw->role = g_key_file_get_string (key_file, window_group, TERMINAL_CONFIG_WINDOW_PROP_ROLE, NULL); + iw->geometry = g_key_file_get_string (key_file, window_group, TERMINAL_CONFIG_WINDOW_PROP_GEOMETRY, NULL); + iw->start_fullscreen = g_key_file_get_boolean (key_file, window_group, TERMINAL_CONFIG_WINDOW_PROP_FULLSCREEN, NULL); + iw->start_maximized = g_key_file_get_boolean (key_file, window_group, TERMINAL_CONFIG_WINDOW_PROP_MAXIMIZED, NULL); + if (g_key_file_has_key (key_file, window_group, TERMINAL_CONFIG_WINDOW_PROP_MENUBAR_VISIBLE, NULL)) + { + iw->force_menubar_state = TRUE; + iw->menubar_state = g_key_file_get_boolean (key_file, window_group, TERMINAL_CONFIG_WINDOW_PROP_MENUBAR_VISIBLE, NULL); + } + + for (j = 0; tab_groups[j]; ++j) + { + const char *tab_group = tab_groups[j]; + InitialTab *it; + char *profile; + + profile = g_key_file_get_string (key_file, tab_group, TERMINAL_CONFIG_TERMINAL_PROP_PROFILE_ID, NULL); + it = initial_tab_new (profile, TRUE); + g_free (profile); + + iw->tabs = g_list_append (iw->tabs, it); + +/* it->width = g_key_file_get_integer (key_file, tab_group, TERMINAL_CONFIG_TERMINAL_PROP_WIDTH, NULL); + it->height = g_key_file_get_integer (key_file, tab_group, TERMINAL_CONFIG_TERMINAL_PROP_HEIGHT, NULL);*/ + it->working_dir = terminal_util_key_file_get_string_unescape (key_file, tab_group, TERMINAL_CONFIG_TERMINAL_PROP_WORKING_DIRECTORY, NULL); + it->title = g_key_file_get_string (key_file, tab_group, TERMINAL_CONFIG_TERMINAL_PROP_TITLE, NULL); + + if (g_key_file_has_key (key_file, tab_group, TERMINAL_CONFIG_TERMINAL_PROP_COMMAND, NULL) && + !(it->exec_argv = terminal_util_key_file_get_argv (key_file, tab_group, TERMINAL_CONFIG_TERMINAL_PROP_COMMAND, NULL, error))) + { + have_error = TRUE; + break; + } + } + + g_strfreev (tab_groups); + + if (have_error) + break; + } + + g_strfreev (groups); + + if (have_error) + { + g_list_foreach (initial_windows, (GFunc) initial_window_free, NULL); + g_list_free (initial_windows); + return FALSE; + } + + options->initial_windows = g_list_concat (options->initial_windows, initial_windows); + + return TRUE; +} + +/** + * terminal_options_ensure_window: + * @options: + * + * Ensure that @options will contain at least one window to open. + */ +void +terminal_options_ensure_window (TerminalOptions *options) +{ + ensure_top_window (options); +} + +/** + * terminal_options_free: + * @options: + * + * Frees @options. + */ +void +terminal_options_free (TerminalOptions *options) +{ + g_list_foreach (options->initial_windows, (GFunc) initial_window_free, NULL); + g_list_free (options->initial_windows); + + g_strfreev (options->env); + g_free (options->default_role); + g_free (options->default_geometry); + g_free (options->default_working_dir); + g_free (options->default_title); + g_free (options->default_profile); + + g_strfreev (options->exec_argv); + + g_free (options->display_name); + g_free (options->startup_id); + + g_slice_free (TerminalOptions, options); +} + +static GOptionContext * +get_goption_context (TerminalOptions *options) +{ + const GOptionEntry global_unique_goptions[] = { + { + "disable-factory", + 0, + G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + option_disable_factory_callback, + N_("Do not register with the activation nameserver, do not re-use an active terminal"), + NULL + }, + { + "load-config", + 0, + G_OPTION_FLAG_FILENAME, + G_OPTION_ARG_CALLBACK, + option_load_save_config_cb, + N_("Load a terminal configuration file"), + N_("FILE") + }, + { + "save-config", + 0, + G_OPTION_FLAG_FILENAME, + G_OPTION_ARG_CALLBACK, + option_load_save_config_cb, + N_("Save the terminal configuration to a file"), + N_("FILE") + }, + { "version", 0, G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, option_version_cb, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL, NULL } + }; + + const GOptionEntry global_multiple_goptions[] = { + { + "window", + 0, + G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + option_window_callback, + N_("Open a new window containing a tab with the default profile"), + NULL + }, + { + "tab", + 0, + G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + option_tab_callback, + N_("Open a new tab in the last-opened window with the default profile"), + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } + }; + + const GOptionEntry window_goptions[] = { + { + "show-menubar", + 0, + G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + option_show_menubar_callback, + N_("Turn on the menubar"), + NULL + }, + { + "hide-menubar", + 0, + G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + option_hide_menubar_callback, + N_("Turn off the menubar"), + NULL + }, + { + "maximize", + 0, + G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + option_maximize_callback, + N_("Maximise the window"), + NULL + }, + { + "full-screen", + 0, + G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + option_fullscreen_callback, + N_("Full-screen the window"), + NULL + }, + { + "geometry", + 0, + 0, + G_OPTION_ARG_CALLBACK, + option_geometry_callback, + N_("Set the window size; for example: 80x24, or 80x24+200+200 (ROWSxCOLS+X+Y)"), + N_("GEOMETRY") + }, + { + "role", + 0, + 0, + G_OPTION_ARG_CALLBACK, + option_role_callback, + N_("Set the window role"), + N_("ROLE") + }, + { + "active", + 0, + G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + option_active_callback, + N_("Set the last specified tab as the active one in its window"), + NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } + }; + + const GOptionEntry terminal_goptions[] = { + { + "command", + 'e', + G_OPTION_FLAG_FILENAME, + G_OPTION_ARG_CALLBACK, + option_command_callback, + N_("Execute the argument to this option inside the terminal"), + NULL + }, + { + "profile", + 0, + 0, + G_OPTION_ARG_CALLBACK, + option_profile_cb, + N_("Use the given profile instead of the default profile"), + N_("PROFILE-NAME") + }, + { + "title", + 't', + 0, + G_OPTION_ARG_CALLBACK, + option_title_callback, + N_("Set the terminal title"), + N_("TITLE") + }, + { + "working-directory", + 0, + G_OPTION_FLAG_FILENAME, + G_OPTION_ARG_CALLBACK, + option_working_directory_callback, + N_("Set the working directory"), + N_("DIRNAME") + }, + { + "zoom", + 0, + 0, + G_OPTION_ARG_CALLBACK, + option_zoom_callback, + N_("Set the terminal's zoom factor (1.0 = normal size)"), + N_("ZOOM") + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } + }; + + const GOptionEntry internal_goptions[] = { + { + "profile-id", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, + option_profile_id_cb, + NULL, NULL + }, + { + "window-with-profile", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, + option_window_callback, + NULL, NULL + }, + { + "tab-with-profile", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, + option_tab_callback, + NULL, NULL + }, + { + "window-with-profile-internal-id", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, + option_window_callback, + NULL, NULL + }, + { + "tab-with-profile-internal-id", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, + option_tab_callback, + NULL, NULL + }, + { + "default-working-directory", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_FILENAME, + &options->default_working_dir, + NULL, NULL, + }, + { + "use-factory", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, + &options->use_factory, + NULL, NULL + }, + { + "startup-id", + 0, + G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_STRING, + &options->startup_id, + NULL, + NULL + }, + /* + * Crappy old compat args + */ + { + "tclass", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "font", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "nologin", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "login", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "foreground", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "background", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "solid", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "bgscroll", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "bgnoscroll", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "shaded", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "noshaded", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "transparent", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "utmp", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "noutmp", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "wtmp", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "nowtmp", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "lastlog", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "nolastlog", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "icon", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "termname", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { + "start-factory-server", + 0, + G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, + unsupported_option_callback, + NULL, NULL + }, + { NULL, 0, 0, 0, NULL, NULL, NULL } + }; + + GOptionContext *context; + GOptionGroup *group; + + context = g_option_context_new (NULL); + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); + g_option_context_set_description (context, N_("MATE Terminal Emulator")); + + group = g_option_group_new ("mate-terminal", + N_("MATE Terminal Emulator"), + N_("Show MATE Terminal options"), + options, + NULL); + g_option_group_set_translation_domain (group, GETTEXT_PACKAGE); + g_option_group_add_entries (group, global_unique_goptions); + g_option_group_add_entries (group, internal_goptions); + g_option_group_set_parse_hooks (group, NULL, digest_options_callback); + g_option_context_set_main_group (context, group); + + group = g_option_group_new ("terminal", + N_("Options to open new windows or terminal tabs; more than one of these may be specified:"), + N_("Show terminal options"), + options, + NULL); + g_option_group_set_translation_domain (group, GETTEXT_PACKAGE); + g_option_group_add_entries (group, global_multiple_goptions); + g_option_context_add_group (context, group); + + group = g_option_group_new ("window-options", + N_("Window options; if used before the first --window or --tab argument, sets the default for all windows:"), + N_("Show per-window options"), + options, + NULL); + g_option_group_set_translation_domain (group, GETTEXT_PACKAGE); + g_option_group_add_entries (group, window_goptions); + g_option_context_add_group (context, group); + + group = g_option_group_new ("terminal-options", + N_("Terminal options; if used before the first --window or --tab argument, sets the default for all terminals:"), + N_("Show per-terminal options"), + options, + NULL); + g_option_group_set_translation_domain (group, GETTEXT_PACKAGE); + g_option_group_add_entries (group, terminal_goptions); + g_option_context_add_group (context, group); + + return context; +} diff --git a/src/terminal-options.h b/src/terminal-options.h new file mode 100644 index 0000000..2094ddc --- /dev/null +++ b/src/terminal-options.h @@ -0,0 +1,118 @@ +/* + * Copyright © 2001, 2002 Havoc Pennington + * Copyright © 2002 Red Hat, Inc. + * Copyright © 2002 Sun Microsystems + * Copyright © 2003 Mariano Suarez-Alvarez + * Copyright © 2008 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TERMINAL_OPTIONS_H +#define TERMINAL_OPTIONS_H + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct +{ + gboolean remote_arguments; + char **env; + char *startup_id; + char *display_name; + int screen_number; + GList *initial_windows; + gboolean default_window_menubar_forced; + gboolean default_window_menubar_state; + gboolean default_fullscreen; + gboolean default_maximize; + char *default_role; + char *default_geometry; + char *default_working_dir; + char *default_title; + char **exec_argv; + char *default_profile; + gboolean default_profile_is_id; + + gboolean execute; + gboolean use_factory; + double zoom; + + char *config_file; + gboolean load_config; + gboolean save_config; +} TerminalOptions; + +typedef struct +{ + char *profile; + gboolean profile_is_id; + char **exec_argv; + char *title; + char *working_dir; + double zoom; + guint zoom_set : 1; + guint active : 1; +} InitialTab; + +typedef struct +{ + guint source_tag; + + GList *tabs; /* list of InitialTab */ + + gboolean force_menubar_state; + gboolean menubar_state; + + gboolean start_fullscreen; + gboolean start_maximized; + + char *geometry; + char *role; + +} InitialWindow; + +#define TERMINAL_OPTION_ERROR (g_quark_from_static_string ("terminal-option-error")) + +typedef enum { + TERMINAL_OPTION_ERROR_NOT_IN_FACTORY, + TERMINAL_OPTION_ERROR_EXCLUSIVE_OPTIONS, + TERMINAL_OPTION_ERROR_INVALID_CONFIG_FILE, + TERMINAL_OPTION_ERROR_INCOMPATIBLE_CONFIG_FILE +} TerminalOptionError; + +TerminalOptions *terminal_options_parse (const char *working_directory, + const char *display_name, + const char *startup_id, + char **env, + gboolean remote_arguments, + gboolean ignore_unknown_options, + int *argcp, + char ***argvp, + GError **error, + ...) G_GNUC_NULL_TERMINATED; + +gboolean terminal_options_merge_config (TerminalOptions *options, + GKeyFile *key_file, + guint source_tag, + GError **error); + +void terminal_options_ensure_window (TerminalOptions *options); + +void terminal_options_free (TerminalOptions *options); + +G_END_DECLS + +#endif /* !TERMINAL_OPTIONS_H */ diff --git a/src/terminal-profile.c b/src/terminal-profile.c new file mode 100644 index 0000000..61bab09 --- /dev/null +++ b/src/terminal-profile.c @@ -0,0 +1,1690 @@ +/* + * Copyright © 2001 Havoc Pennington + * Copyright © 2002 Mathias Hasselmann + * Copyright © 2008 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include <gtk/gtk.h> + +#include <mateconf/mateconf-client.h> + +#include "terminal-app.h" +#include "terminal-debug.h" +#include "terminal-intl.h" +#include "terminal-profile.h" +#include "terminal-screen.h" +#include "terminal-type-builtins.h" + +/* To add a new key, you need to: + * + * - add an entry to the enum below + * - add a #define with its name in terminal-profile.h + * - add a gobject property for it in terminal_profile_class_init + * - if the property's type needs special casing, add that to + * terminal_profile_mateconf_notify_cb and + * terminal_profile_mateconf_changeset_add + * - if necessary the default value cannot be handled via the paramspec, + * handle that in terminal_profile_reset_property_internal + */ +enum +{ + PROP_0, + PROP_ALLOW_BOLD, + PROP_BACKGROUND_COLOR, + PROP_BACKGROUND_DARKNESS, + PROP_BACKGROUND_IMAGE, + PROP_BACKGROUND_IMAGE_FILE, + PROP_BACKGROUND_TYPE, + PROP_BACKSPACE_BINDING, + PROP_BOLD_COLOR, + PROP_BOLD_COLOR_SAME_AS_FG, + PROP_CURSOR_BLINK_MODE, + PROP_CURSOR_SHAPE, + PROP_CUSTOM_COMMAND, + PROP_DEFAULT_SIZE_COLUMNS, + PROP_DEFAULT_SIZE_ROWS, + PROP_DEFAULT_SHOW_MENUBAR, + PROP_DELETE_BINDING, + PROP_EXIT_ACTION, + PROP_FONT, + PROP_FOREGROUND_COLOR, + PROP_LOGIN_SHELL, + PROP_NAME, + PROP_PALETTE, + PROP_SCROLL_BACKGROUND, + PROP_SCROLLBACK_LINES, + PROP_SCROLLBACK_UNLIMITED, + PROP_SCROLLBAR_POSITION, + PROP_SCROLL_ON_KEYSTROKE, + PROP_SCROLL_ON_OUTPUT, + PROP_SILENT_BELL, + PROP_TITLE, + PROP_TITLE_MODE, + PROP_UPDATE_RECORDS, + PROP_USE_CUSTOM_COMMAND, + PROP_USE_CUSTOM_DEFAULT_SIZE, + PROP_USE_SKEY, + PROP_USE_SYSTEM_FONT, + PROP_USE_THEME_COLORS, + PROP_VISIBLE_NAME, + PROP_WORD_CHARS, + LAST_PROP +}; + +#define KEY_ALLOW_BOLD "allow_bold" +#define KEY_BACKGROUND_COLOR "background_color" +#define KEY_BACKGROUND_DARKNESS "background_darkness" +#define KEY_BACKGROUND_IMAGE_FILE "background_image" +#define KEY_BACKGROUND_TYPE "background_type" +#define KEY_BACKSPACE_BINDING "backspace_binding" +#define KEY_BOLD_COLOR "bold_color" +#define KEY_BOLD_COLOR_SAME_AS_FG "bold_color_same_as_fg" +#define KEY_CURSOR_BLINK_MODE "cursor_blink_mode" +#define KEY_CURSOR_SHAPE "cursor_shape" +#define KEY_CUSTOM_COMMAND "custom_command" +#define KEY_DEFAULT_SHOW_MENUBAR "default_show_menubar" +#define KEY_DEFAULT_SIZE_COLUMNS "default_size_columns" +#define KEY_DEFAULT_SIZE_ROWS "default_size_rows" +#define KEY_DELETE_BINDING "delete_binding" +#define KEY_EXIT_ACTION "exit_action" +#define KEY_FONT "font" +#define KEY_FOREGROUND_COLOR "foreground_color" +#define KEY_LOGIN_SHELL "login_shell" +#define KEY_PALETTE "palette" +#define KEY_SCROLL_BACKGROUND "scroll_background" +#define KEY_SCROLLBACK_LINES "scrollback_lines" +#define KEY_SCROLLBACK_UNLIMITED "scrollback_unlimited" +#define KEY_SCROLLBAR_POSITION "scrollbar_position" +#define KEY_SCROLL_ON_KEYSTROKE "scroll_on_keystroke" +#define KEY_SCROLL_ON_OUTPUT "scroll_on_output" +#define KEY_SILENT_BELL "silent_bell" +#define KEY_TITLE_MODE "title_mode" +#define KEY_TITLE "title" +#define KEY_UPDATE_RECORDS "update_records" +#define KEY_USE_CUSTOM_COMMAND "use_custom_command" +#define KEY_USE_CUSTOM_DEFAULT_SIZE "use_custom_default_size" +#define KEY_USE_SKEY "use_skey" +#define KEY_USE_SYSTEM_FONT "use_system_font" +#define KEY_USE_THEME_COLORS "use_theme_colors" +#define KEY_VISIBLE_NAME "visible_name" +#define KEY_WORD_CHARS "word_chars" + +/* Keep these in sync with the MateConf schema! */ +#define DEFAULT_ALLOW_BOLD (TRUE) +#define DEFAULT_BACKGROUND_COLOR ("#FFFFDD") +#define DEFAULT_BOLD_COLOR_SAME_AS_FG (TRUE) +#define DEFAULT_BACKGROUND_DARKNESS (0.5) +#define DEFAULT_BACKGROUND_IMAGE_FILE ("") +#define DEFAULT_BACKGROUND_IMAGE (NULL) +#define DEFAULT_BACKGROUND_TYPE (TERMINAL_BACKGROUND_SOLID) +#define DEFAULT_BACKSPACE_BINDING (VTE_ERASE_ASCII_DELETE) +#define DEFAULT_CURSOR_BLINK_MODE (VTE_CURSOR_BLINK_SYSTEM) +#define DEFAULT_CURSOR_SHAPE (VTE_CURSOR_SHAPE_BLOCK) +#define DEFAULT_CUSTOM_COMMAND ("") +#define DEFAULT_DEFAULT_SHOW_MENUBAR (TRUE) +#define DEFAULT_DEFAULT_SIZE_COLUMNS (80) +#define DEFAULT_DEFAULT_SIZE_ROWS (24) +#define DEFAULT_DELETE_BINDING (VTE_ERASE_DELETE_SEQUENCE) +#define DEFAULT_EXIT_ACTION (TERMINAL_EXIT_CLOSE) +#define DEFAULT_FONT ("Monospace 12") +#define DEFAULT_FOREGROUND_COLOR ("#000000") +#define DEFAULT_LOGIN_SHELL (FALSE) +#define DEFAULT_NAME (NULL) +#define DEFAULT_PALETTE (terminal_palettes[TERMINAL_PALETTE_TANGO]) +#define DEFAULT_SCROLL_BACKGROUND (TRUE) +#define DEFAULT_SCROLLBACK_LINES (512) +#define DEFAULT_SCROLLBACK_UNLIMITED (FALSE) +#define DEFAULT_SCROLLBAR_POSITION (TERMINAL_SCROLLBAR_RIGHT) +#define DEFAULT_SCROLL_ON_KEYSTROKE (TRUE) +#define DEFAULT_SCROLL_ON_OUTPUT (FALSE) +#define DEFAULT_SILENT_BELL (FALSE) +#define DEFAULT_TITLE_MODE (TERMINAL_TITLE_REPLACE) +#define DEFAULT_TITLE (N_("Terminal")) +#define DEFAULT_UPDATE_RECORDS (TRUE) +#define DEFAULT_USE_CUSTOM_COMMAND (FALSE) +#define DEFAULT_USE_CUSTOM_DEFAULT_SIZE (FALSE) +#define DEFAULT_USE_SKEY (TRUE) +#define DEFAULT_USE_SYSTEM_FONT (TRUE) +#define DEFAULT_USE_THEME_COLORS (TRUE) +#define DEFAULT_VISIBLE_NAME (N_("Unnamed")) +#define DEFAULT_WORD_CHARS ("-A-Za-z0-9,./?%&#:_=+@~") + +struct _TerminalProfilePrivate +{ + GValueArray *properties; + gboolean *locked; + + MateConfClient *conf; + char *profile_dir; + guint notify_id; + + GSList *dirty_pspecs; + guint save_idle_id; + + GParamSpec *mateconf_notification_pspec; + + gboolean background_load_failed; + + guint forgotten : 1; +}; + +/* We have to continue to use these since they're unfortunately different + * from the value nicks of the vte_terminal_erase_binding_get_type() enum type. + */ +static const MateConfEnumStringPair erase_bindings[] = { + { VTE_ERASE_AUTO, "auto" }, + { VTE_ERASE_ASCII_BACKSPACE, "control-h" }, + { VTE_ERASE_ASCII_DELETE, "ascii-del" }, + { VTE_ERASE_DELETE_SEQUENCE, "escape-sequence" }, + { VTE_ERASE_TTY, "tty" }, + { -1, NULL } +}; + +static const GdkColor terminal_palettes[TERMINAL_PALETTE_N_BUILTINS][TERMINAL_PALETTE_SIZE] = +{ + /* Tango palette */ + { + { 0, 0x0000, 0x0000, 0x0000 }, + { 0, 0xcccc, 0x0000, 0x0000 }, + { 0, 0x4e4e, 0x9a9a, 0x0606 }, + { 0, 0xc4c4, 0xa0a0, 0x0000 }, + { 0, 0x3434, 0x6565, 0xa4a4 }, + { 0, 0x7575, 0x5050, 0x7b7b }, + { 0, 0x0606, 0x9820, 0x9a9a }, + { 0, 0xd3d3, 0xd7d7, 0xcfcf }, + { 0, 0x5555, 0x5757, 0x5353 }, + { 0, 0xefef, 0x2929, 0x2929 }, + { 0, 0x8a8a, 0xe2e2, 0x3434 }, + { 0, 0xfcfc, 0xe9e9, 0x4f4f }, + { 0, 0x7272, 0x9f9f, 0xcfcf }, + { 0, 0xadad, 0x7f7f, 0xa8a8 }, + { 0, 0x3434, 0xe2e2, 0xe2e2 }, + { 0, 0xeeee, 0xeeee, 0xecec } + }, + + /* Linux palette */ + { + { 0, 0x0000, 0x0000, 0x0000 }, + { 0, 0xaaaa, 0x0000, 0x0000 }, + { 0, 0x0000, 0xaaaa, 0x0000 }, + { 0, 0xaaaa, 0x5555, 0x0000 }, + { 0, 0x0000, 0x0000, 0xaaaa }, + { 0, 0xaaaa, 0x0000, 0xaaaa }, + { 0, 0x0000, 0xaaaa, 0xaaaa }, + { 0, 0xaaaa, 0xaaaa, 0xaaaa }, + { 0, 0x5555, 0x5555, 0x5555 }, + { 0, 0xffff, 0x5555, 0x5555 }, + { 0, 0x5555, 0xffff, 0x5555 }, + { 0, 0xffff, 0xffff, 0x5555 }, + { 0, 0x5555, 0x5555, 0xffff }, + { 0, 0xffff, 0x5555, 0xffff }, + { 0, 0x5555, 0xffff, 0xffff }, + { 0, 0xffff, 0xffff, 0xffff } + }, + + /* XTerm palette */ + { + { 0, 0x0000, 0x0000, 0x0000 }, + { 0, 0xcdcb, 0x0000, 0x0000 }, + { 0, 0x0000, 0xcdcb, 0x0000 }, + { 0, 0xcdcb, 0xcdcb, 0x0000 }, + { 0, 0x1e1a, 0x908f, 0xffff }, + { 0, 0xcdcb, 0x0000, 0xcdcb }, + { 0, 0x0000, 0xcdcb, 0xcdcb }, + { 0, 0xe5e2, 0xe5e2, 0xe5e2 }, + { 0, 0x4ccc, 0x4ccc, 0x4ccc }, + { 0, 0xffff, 0x0000, 0x0000 }, + { 0, 0x0000, 0xffff, 0x0000 }, + { 0, 0xffff, 0xffff, 0x0000 }, + { 0, 0x4645, 0x8281, 0xb4ae }, + { 0, 0xffff, 0x0000, 0xffff }, + { 0, 0x0000, 0xffff, 0xffff }, + { 0, 0xffff, 0xffff, 0xffff } + }, + + /* RXVT palette */ + { + { 0, 0x0000, 0x0000, 0x0000 }, + { 0, 0xcdcd, 0x0000, 0x0000 }, + { 0, 0x0000, 0xcdcd, 0x0000 }, + { 0, 0xcdcd, 0xcdcd, 0x0000 }, + { 0, 0x0000, 0x0000, 0xcdcd }, + { 0, 0xcdcd, 0x0000, 0xcdcd }, + { 0, 0x0000, 0xcdcd, 0xcdcd }, + { 0, 0xfafa, 0xebeb, 0xd7d7 }, + { 0, 0x4040, 0x4040, 0x4040 }, + { 0, 0xffff, 0x0000, 0x0000 }, + { 0, 0x0000, 0xffff, 0x0000 }, + { 0, 0xffff, 0xffff, 0x0000 }, + { 0, 0x0000, 0x0000, 0xffff }, + { 0, 0xffff, 0x0000, 0xffff }, + { 0, 0x0000, 0xffff, 0xffff }, + { 0, 0xffff, 0xffff, 0xffff } + } +}; + +static const GdkColor default_fg_color = { 0, 0, 0, 0 }; +static const GdkColor default_bg_color = { 0, 0xffff, 0xffff, 0xdddd }; + +enum +{ + FORGOTTEN, + LAST_SIGNAL +}; + +static void terminal_profile_finalize (GObject *object); +static void terminal_profile_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void ensure_pixbuf_property (TerminalProfile *profile, + guint path_prop_id, + guint pixbuf_prop_id, + gboolean *load_failed); + +static guint signals[LAST_SIGNAL]; +static GQuark mateconf_key_quark; + +G_DEFINE_TYPE (TerminalProfile, terminal_profile, G_TYPE_OBJECT); + +static gboolean +palette_cmp (const GdkColor *ca, + const GdkColor *cb) +{ + guint i; + + for (i = 0; i < TERMINAL_PALETTE_SIZE; ++i) + if (!gdk_color_equal (&ca[i], &cb[i])) + return FALSE; + + return TRUE; +} + +static GParamSpec * +get_pspec_from_name (TerminalProfile *profile, + const char *prop_name) +{ + TerminalProfileClass *klass = TERMINAL_PROFILE_GET_CLASS (profile); + GParamSpec *pspec; + + pspec = g_object_class_find_property (G_OBJECT_CLASS (klass), prop_name); + if (pspec && + pspec->owner_type != TERMINAL_TYPE_PROFILE) + pspec = NULL; + + return pspec; +} + +static const GValue * +get_prop_value_from_prop_name (TerminalProfile *profile, + const char *prop_name) +{ + TerminalProfilePrivate *priv = profile->priv; + GParamSpec *pspec; + + pspec = get_pspec_from_name (profile, prop_name); + if (!pspec) + return NULL; + + if (G_UNLIKELY (pspec->param_id == PROP_BACKGROUND_IMAGE)) + ensure_pixbuf_property (profile, PROP_BACKGROUND_IMAGE_FILE, PROP_BACKGROUND_IMAGE, &priv->background_load_failed); + + return g_value_array_get_nth (priv->properties, pspec->param_id); +} + +static void +set_value_from_palette (GValue *ret_value, + const GdkColor *colors, + guint n_colors) +{ + GValueArray *array; + guint i, max_n_colors; + + max_n_colors = MAX (n_colors, TERMINAL_PALETTE_SIZE); + array = g_value_array_new (max_n_colors); + for (i = 0; i < max_n_colors; ++i) + g_value_array_append (array, NULL); + + for (i = 0; i < n_colors; ++i) + { + GValue *value = g_value_array_get_nth (array, i); + + g_value_init (value, GDK_TYPE_COLOR); + g_value_set_boxed (value, &colors[i]); + } + + /* If we haven't enough colours yet, fill up with the default palette */ + for (i = n_colors; i < TERMINAL_PALETTE_SIZE; ++i) + { + GValue *value = g_value_array_get_nth (array, i); + + g_value_init (value, GDK_TYPE_COLOR); + g_value_set_boxed (value, &DEFAULT_PALETTE[i]); + } + + g_value_take_boxed (ret_value, array); +} + +static int +values_equal (GParamSpec *pspec, + const GValue *va, + const GValue *vb) +{ + /* g_param_values_cmp isn't good enough for some types, since e.g. + * it compares colours and font descriptions by pointer value, not + * with the correct compare functions. Providing extra + * PangoParamSpecFontDescription and GdkParamSpecColor wouldn't + * have fixed this either, since it's unclear how to _order_ them. + * Luckily we only need to check them for equality here. + */ + + if (g_param_values_cmp (pspec, va, vb) == 0) + return TRUE; + + if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_COLOR) + return gdk_color_equal (g_value_get_boxed (va), g_value_get_boxed (vb)); + + if (G_PARAM_SPEC_VALUE_TYPE (pspec) == PANGO_TYPE_FONT_DESCRIPTION) + return pango_font_description_equal (g_value_get_boxed (va), g_value_get_boxed (vb)); + + if (G_IS_PARAM_SPEC_VALUE_ARRAY (pspec) && + G_PARAM_SPEC_VALUE_TYPE (G_PARAM_SPEC_VALUE_ARRAY (pspec)->element_spec) == GDK_TYPE_COLOR) + { + GValueArray *ara, *arb; + guint i; + + ara = g_value_get_boxed (va); + arb = g_value_get_boxed (vb); + + if (!ara || !arb || ara->n_values != arb->n_values) + return FALSE; + + for (i = 0; i < ara->n_values; ++i) + if (!gdk_color_equal (g_value_get_boxed (g_value_array_get_nth (ara, i)), + g_value_get_boxed (g_value_array_get_nth (arb, i)))) + return FALSE; + + return TRUE; + } + + return FALSE; +} + +static void +ensure_pixbuf_property (TerminalProfile *profile, + guint path_prop_id, + guint pixbuf_prop_id, + gboolean *load_failed) +{ + TerminalProfilePrivate *priv = profile->priv; + GValue *path_value, *pixbuf_value; + GdkPixbuf *pixbuf; + const char *path_utf8; + char *path; + GError *error = NULL; + + pixbuf_value = g_value_array_get_nth (priv->properties, pixbuf_prop_id); + + pixbuf = g_value_get_object (pixbuf_value); + if (pixbuf) + return; + + if (*load_failed) + return; + + path_value = g_value_array_get_nth (priv->properties, path_prop_id); + path_utf8 = g_value_get_string (path_value); + if (!path_utf8 || !path_utf8[0]) + goto failed; + + path = g_filename_from_utf8 (path_utf8, -1, NULL, NULL, NULL); + if (!path) + goto failed; + + pixbuf = gdk_pixbuf_new_from_file (path, &error); + if (!pixbuf) + { + _terminal_debug_print (TERMINAL_DEBUG_PROFILE, + "Failed to load image \"%s\": %s\n", + path, error->message); + + g_error_free (error); + g_free (path); + goto failed; + } + + g_value_take_object (pixbuf_value, pixbuf); + g_free (path); + return; + +failed: + *load_failed = TRUE; +} + +static void +terminal_profile_reset_property_internal (TerminalProfile *profile, + GParamSpec *pspec, + gboolean notify) +{ + TerminalProfilePrivate *priv = profile->priv; + GValue value_ = { 0, }; + GValue *value; + + if (notify) + { + value = &value_; + g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + } + else + value = g_value_array_get_nth (priv->properties, pspec->param_id); + g_assert (value != NULL); + + /* A few properties don't have defaults via the param spec; set them explicitly */ + switch (pspec->param_id) + { + case PROP_FOREGROUND_COLOR: + case PROP_BOLD_COLOR: + g_value_set_boxed (value, &DEFAULT_FOREGROUND_COLOR); + break; + + case PROP_BACKGROUND_COLOR: + g_value_set_boxed (value, &DEFAULT_BACKGROUND_COLOR); + break; + + case PROP_FONT: + g_value_take_boxed (value, pango_font_description_from_string (DEFAULT_FONT)); + break; + + case PROP_PALETTE: + set_value_from_palette (value, DEFAULT_PALETTE, TERMINAL_PALETTE_SIZE); + break; + + default: + g_param_value_set_default (pspec, value); + break; + } + + if (notify) + { + g_object_set_property (G_OBJECT (profile), pspec->name, value); + g_value_unset (value); + } +} + +static void +terminal_profile_mateconf_notify_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + TerminalProfile *profile = TERMINAL_PROFILE (user_data); + TerminalProfilePrivate *priv = profile->priv; + TerminalProfileClass *klass; + const char *key; + MateConfValue *mateconf_value; + GParamSpec *pspec; + GValue value = { 0, }; + gboolean equal; + gboolean force_set = FALSE; + + key = mateconf_entry_get_key (entry); + if (!key || !g_str_has_prefix (key, priv->profile_dir)) + return; + + _terminal_debug_print (TERMINAL_DEBUG_PROFILE, + "MateConf notification for key %s [%s]\n", + key, + mateconf_entry_get_is_writable (entry) ? "writable" : "LOCKED"); + + key += strlen (priv->profile_dir); + if (!key[0]) + return; + + key++; + klass = TERMINAL_PROFILE_GET_CLASS (profile); + pspec = g_hash_table_lookup (klass->mateconf_keys, key); + if (!pspec) + return; /* ignore unknown keys, for future extensibility */ + + priv->locked[pspec->param_id] = !mateconf_entry_get_is_writable (entry); + + mateconf_value = mateconf_entry_get_value (entry); + if (!mateconf_value) + return; + + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + + if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) + { + if (mateconf_value->type != MATECONF_VALUE_BOOL) + goto out; + + g_value_set_boolean (&value, mateconf_value_get_bool (mateconf_value)); + } + else if (G_IS_PARAM_SPEC_STRING (pspec)) + { + if (mateconf_value->type != MATECONF_VALUE_STRING) + goto out; + + g_value_set_string (&value, mateconf_value_get_string (mateconf_value)); + } + else if (G_IS_PARAM_SPEC_ENUM (pspec)) + { + const GEnumValue *eval; + int enum_value; + + if (mateconf_value->type != MATECONF_VALUE_STRING) + goto out; + + eval = g_enum_get_value_by_nick (G_PARAM_SPEC_ENUM (pspec)->enum_class, + mateconf_value_get_string (mateconf_value)); + if (eval) + enum_value = eval->value; + else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == vte_terminal_erase_binding_get_type ()) + { + /* Backward compatibility */ + if (!mateconf_string_to_enum ((MateConfEnumStringPair*) erase_bindings, + mateconf_value_get_string (mateconf_value), + &enum_value)) + goto out; + } + else + goto out; + + g_value_set_enum (&value, enum_value); + } + else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_COLOR) + { + GdkColor color; + + if (mateconf_value->type != MATECONF_VALUE_STRING) + goto out; + + if (!gdk_color_parse (mateconf_value_get_string (mateconf_value), &color)) + goto out; + + g_value_set_boxed (&value, &color); + } + else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == PANGO_TYPE_FONT_DESCRIPTION) + { + if (mateconf_value->type != MATECONF_VALUE_STRING) + goto out; + + g_value_take_boxed (&value, pango_font_description_from_string (mateconf_value_get_string (mateconf_value))); + } + else if (G_IS_PARAM_SPEC_DOUBLE (pspec)) + { + if (mateconf_value->type != MATECONF_VALUE_FLOAT) + goto out; + + g_value_set_double (&value, mateconf_value_get_float (mateconf_value)); + } + else if (G_IS_PARAM_SPEC_INT (pspec)) + { + if (mateconf_value->type != MATECONF_VALUE_INT) + goto out; + + g_value_set_int (&value, mateconf_value_get_int (mateconf_value)); + } + else if (G_IS_PARAM_SPEC_VALUE_ARRAY (pspec) && + G_PARAM_SPEC_VALUE_TYPE (G_PARAM_SPEC_VALUE_ARRAY (pspec)->element_spec) == GDK_TYPE_COLOR) + { + char **color_strings; + GdkColor *colors; + int n_colors, i; + + if (mateconf_value->type != MATECONF_VALUE_STRING) + goto out; + + color_strings = g_strsplit (mateconf_value_get_string (mateconf_value), ":", -1); + if (!color_strings) + goto out; + + n_colors = g_strv_length (color_strings); + colors = g_new0 (GdkColor, n_colors); + for (i = 0; i < n_colors; ++i) + { + if (!gdk_color_parse (color_strings[i], &colors[i])) + continue; /* ignore errors */ + } + g_strfreev (color_strings); + + /* We continue even with a palette size != TERMINAL_PALETTE_SIZE, + * so we can change the palette size in future versions without + * causing too many issues. + */ + set_value_from_palette (&value, colors, n_colors); + g_free (colors); + } + else + { + g_printerr ("Unhandled value type %s of pspec %s\n", g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), pspec->name); + goto out; + } + + if (g_param_value_validate (pspec, &value)) + { + _terminal_debug_print (TERMINAL_DEBUG_PROFILE, + "Invalid value in mateconf for key %s was changed to comply with pspec %s\n", + mateconf_entry_get_key (entry), pspec->name); + + force_set = TRUE; + } + + /* Only set the property if the value is different than our current value, + * so we don't go into an infinite loop. + */ + equal = values_equal (pspec, &value, g_value_array_get_nth (priv->properties, pspec->param_id)); +#ifdef MATE_ENABLE_DEBUG + _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_PROFILE) + { + if (!equal) + _terminal_debug_print (TERMINAL_DEBUG_PROFILE, + "Setting property %s to a different value\n" + " now: %s\n" + " new: %s\n", + pspec->name, + g_strdup_value_contents (g_value_array_get_nth (priv->properties, pspec->param_id)), + g_strdup_value_contents (&value)); + } +#endif + + if (!equal || force_set) + { + priv->mateconf_notification_pspec = pspec; + g_object_set_property (G_OBJECT (profile), pspec->name, &value); + priv->mateconf_notification_pspec = NULL; + } + +out: + /* FIXME: if we arrive here through goto in the error cases, + * should we maybe reset the property to its default value? + */ + + g_value_unset (&value); +} + +static void +terminal_profile_mateconf_changeset_add (TerminalProfile *profile, + MateConfChangeSet *changeset, + GParamSpec *pspec) +{ + TerminalProfilePrivate *priv = profile->priv; + const char *mateconf_key; + char *key; + const GValue *value; + + /* FIXME: do this? */ +#if 0 + if (priv->locked[pspec->param_id]) + return; + + if (!mateconf_client_key_is_writable (priv->conf, mateconf_key, NULL)) + return; +#endif + + mateconf_key = g_param_spec_get_qdata (pspec, mateconf_key_quark); + if (!mateconf_key) + return; + + key = mateconf_concat_dir_and_key (priv->profile_dir, mateconf_key); + value = g_value_array_get_nth (priv->properties, pspec->param_id); + + _terminal_debug_print (TERMINAL_DEBUG_PROFILE, + "Adding pspec %s with value %s to the mateconf changeset\n", + pspec->name, g_strdup_value_contents (value)); + + if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) + mateconf_change_set_set_bool (changeset, key, g_value_get_boolean (value)); + else if (G_IS_PARAM_SPEC_STRING (pspec)) + { + const char *str; + + str = g_value_get_string (value); + mateconf_change_set_set_string (changeset, key, str ? str : ""); + } + else if (G_IS_PARAM_SPEC_ENUM (pspec)) + { + const GEnumValue *eval; + const char *string; + + eval = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, g_value_get_enum (value)); + + if (G_PARAM_SPEC_VALUE_TYPE (pspec) == vte_terminal_erase_binding_get_type ()) + { + /* Backward compatibility */ + string = mateconf_enum_to_string ((MateConfEnumStringPair*) erase_bindings, g_value_get_enum (value)); + if (!string) + goto cleanup; + } + else if (eval) + string = eval->value_nick; + else + goto cleanup; + + mateconf_change_set_set_string (changeset, key, string); + } + else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_COLOR) + { + GdkColor *color; + char str[16]; + + color = g_value_get_boxed (value); + if (!color) + goto cleanup; + + g_snprintf (str, sizeof (str), + "#%04X%04X%04X", + color->red, + color->green, + color->blue); + + mateconf_change_set_set_string (changeset, key, str); + } + else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == PANGO_TYPE_FONT_DESCRIPTION) + { + PangoFontDescription *font_desc; + char *font; + + font_desc = g_value_get_boxed (value); + if (!font_desc) + goto cleanup; + + font = pango_font_description_to_string (font_desc); + mateconf_change_set_set_string (changeset, key, font); + g_free (font); + } + else if (G_IS_PARAM_SPEC_DOUBLE (pspec)) + mateconf_change_set_set_float (changeset, key, (float) g_value_get_double (value)); + else if (G_IS_PARAM_SPEC_INT (pspec)) + mateconf_change_set_set_int (changeset, key, g_value_get_int (value)); + else if (G_IS_PARAM_SPEC_VALUE_ARRAY (pspec) && + G_PARAM_SPEC_VALUE_TYPE (G_PARAM_SPEC_VALUE_ARRAY (pspec)->element_spec) == GDK_TYPE_COLOR) + { + GValueArray *array; + GString *string; + guint n_colors, i; + + /* We need to do this ourselves, because the gtk_color_selection_palette_to_string + * does not carry all the bytes, and xterm's palette is messed up... + */ + + array = g_value_get_boxed (value); + if (!array) + goto cleanup; + + n_colors = array->n_values; + string = g_string_sized_new (n_colors * (1 /* # */ + 3 * 4) + n_colors /* : separators and terminating \0 */); + for (i = 0; i < n_colors; ++i) + { + GdkColor *color; + + if (i > 0) + g_string_append_c (string, ':'); + + color = g_value_get_boxed (g_value_array_get_nth (array, i)); + if (!color) + continue; + + g_string_append_printf (string, + "#%04X%04X%04X", + color->red, + color->green, + color->blue); + } + + mateconf_change_set_set_string (changeset, key, string->str); + g_string_free (string, TRUE); + } + else + g_printerr ("Unhandled value type %s of pspec %s\n", g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), pspec->name); + +cleanup: + g_free (key); +} + +static void +terminal_profile_save (TerminalProfile *profile) +{ + TerminalProfilePrivate *priv = profile->priv; + MateConfChangeSet *changeset; + GSList *l; + GError *error = NULL; + + priv->save_idle_id = 0; + + changeset = mateconf_change_set_new (); + + for (l = priv->dirty_pspecs; l != NULL; l = l->next) + { + GParamSpec *pspec = (GParamSpec *) l->data; + + if (pspec->owner_type != TERMINAL_TYPE_PROFILE) + continue; + + if ((pspec->flags & G_PARAM_WRITABLE) == 0) + continue; + + terminal_profile_mateconf_changeset_add (profile, changeset, pspec); + } + + g_slist_free (priv->dirty_pspecs); + priv->dirty_pspecs = NULL; + + if (!mateconf_client_commit_change_set (priv->conf, changeset, TRUE, &error)) + { + g_message ("Failed to commit the changeset to mateconf: %s", error->message); + g_error_free (error); + } + + mateconf_change_set_unref (changeset); +} + +static gboolean +terminal_profile_save_idle_cb (TerminalProfile *profile) +{ + terminal_profile_save (profile); + + /* don't run again */ + return FALSE; +} + +static void +terminal_profile_schedule_save (TerminalProfile *profile, + GParamSpec *pspec) +{ + TerminalProfilePrivate *priv = profile->priv; + + g_assert (pspec != NULL); + + if (!g_slist_find (priv->dirty_pspecs, pspec)) + priv->dirty_pspecs = g_slist_prepend (priv->dirty_pspecs, pspec); + + if (priv->save_idle_id != 0) + return; + + priv->save_idle_id = g_idle_add ((GSourceFunc) terminal_profile_save_idle_cb, profile); +} + +static void +terminal_profile_init (TerminalProfile *profile) +{ + TerminalProfilePrivate *priv; + GObjectClass *object_class; + GParamSpec **pspecs; + guint n_pspecs, i; + + priv = profile->priv = G_TYPE_INSTANCE_GET_PRIVATE (profile, TERMINAL_TYPE_PROFILE, TerminalProfilePrivate); + + priv->mateconf_notification_pspec = NULL; + + priv->conf = mateconf_client_get_default (); + + priv->locked = g_new0 (gboolean, LAST_PROP); + + priv->properties = g_value_array_new (LAST_PROP); + for (i = 0; i < LAST_PROP; ++i) + g_value_array_append (priv->properties, NULL); + + pspecs = g_object_class_list_properties (G_OBJECT_CLASS (TERMINAL_PROFILE_GET_CLASS (profile)), &n_pspecs); + for (i = 0; i < n_pspecs; ++i) + { + GParamSpec *pspec = pspecs[i]; + GValue *value; + + if (pspec->owner_type != TERMINAL_TYPE_PROFILE) + continue; + + g_assert (pspec->param_id < LAST_PROP); + value = g_value_array_get_nth (priv->properties, pspec->param_id); + g_value_init (value, pspec->value_type); + g_param_value_set_default (pspec, value); + } + + g_free (pspecs); + + /* A few properties don't have defaults via the param spec; set them explicitly */ + object_class = G_OBJECT_CLASS (TERMINAL_PROFILE_GET_CLASS (profile)); + terminal_profile_reset_property_internal (profile, g_object_class_find_property (object_class, TERMINAL_PROFILE_FOREGROUND_COLOR), FALSE); + terminal_profile_reset_property_internal (profile, g_object_class_find_property (object_class, TERMINAL_PROFILE_BOLD_COLOR), FALSE); + terminal_profile_reset_property_internal (profile, g_object_class_find_property (object_class, TERMINAL_PROFILE_BACKGROUND_COLOR), FALSE); + terminal_profile_reset_property_internal (profile, g_object_class_find_property (object_class, TERMINAL_PROFILE_FONT), FALSE); + terminal_profile_reset_property_internal (profile, g_object_class_find_property (object_class, TERMINAL_PROFILE_PALETTE), FALSE); +} + +static GObject * +terminal_profile_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) +{ + GObject *object; + TerminalProfile *profile; + TerminalProfilePrivate *priv; + const char *name; + GParamSpec **pspecs; + guint n_pspecs, i; + + object = G_OBJECT_CLASS (terminal_profile_parent_class)->constructor + (type, n_construct_properties, construct_params); + + profile = TERMINAL_PROFILE (object); + priv = profile->priv; + + name = g_value_get_string (g_value_array_get_nth (priv->properties, PROP_NAME)); + g_assert (name != NULL); + + /* Now load those properties from mateconf that were not set as construction params */ + pspecs = g_object_class_list_properties (G_OBJECT_CLASS (TERMINAL_PROFILE_GET_CLASS (profile)), &n_pspecs); + for (i = 0; i < n_pspecs; ++i) + { + GParamSpec *pspec = pspecs[i]; + guint j; + gboolean is_construct = FALSE; + const char *mateconf_key; + char *key; + + if (pspec->owner_type != TERMINAL_TYPE_PROFILE) + continue; + + if ((pspec->flags & G_PARAM_WRITABLE) == 0 || + (pspec->flags & G_PARAM_CONSTRUCT_ONLY) != 0) + continue; + + for (j = 0; j < n_construct_properties; ++j) + if (pspec == construct_params[j].pspec) + { + is_construct = TRUE; + break; + } + + if (is_construct) + continue; + + mateconf_key = g_param_spec_get_qdata (pspec, mateconf_key_quark); + if (!mateconf_key) + continue; + + key = mateconf_concat_dir_and_key (priv->profile_dir, mateconf_key); + mateconf_client_notify (priv->conf, key); + g_free (key); + } + + g_free (pspecs); + + return object; +} + +static void +terminal_profile_finalize (GObject *object) +{ + TerminalProfile *profile = TERMINAL_PROFILE (object); + TerminalProfilePrivate *priv = profile->priv; + + mateconf_client_notify_remove (priv->conf, priv->notify_id); + priv->notify_id = 0; + + if (priv->save_idle_id) + { + g_source_remove (priv->save_idle_id); + + /* Save now */ + terminal_profile_save (profile); + } + + _terminal_profile_forget (profile); + + g_object_unref (priv->conf); + + g_free (priv->profile_dir); + g_free (priv->locked); + g_value_array_free (priv->properties); + + G_OBJECT_CLASS (terminal_profile_parent_class)->finalize (object); +} + +static void +terminal_profile_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TerminalProfile *profile = TERMINAL_PROFILE (object); + TerminalProfilePrivate *priv = profile->priv; + + if (prop_id == 0 || prop_id >= LAST_PROP) + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + return; + } + + /* Note: When adding things here, do the same in get_prop_value_from_prop_name! */ + switch (prop_id) + { + case PROP_BACKGROUND_IMAGE: + ensure_pixbuf_property (profile, PROP_BACKGROUND_IMAGE_FILE, PROP_BACKGROUND_IMAGE, &priv->background_load_failed); + break; + default: + break; + } + + g_value_copy (g_value_array_get_nth (priv->properties, prop_id), value); +} + +static void +terminal_profile_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TerminalProfile *profile = TERMINAL_PROFILE (object); + TerminalProfilePrivate *priv = profile->priv; + GValue *prop_value; + + if (prop_id == 0 || prop_id >= LAST_PROP) + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + return; + } + + prop_value = g_value_array_get_nth (priv->properties, prop_id); + + /* Preprocessing */ + switch (prop_id) + { +#if 0 + case PROP_FONT: { + PangoFontDescription *font_desc, *new_font_desc; + + font_desc = g_value_get_boxed (prop_value); + new_font_desc = g_value_get_boxed (value); + + if (font_desc && new_font_desc) + { + /* Merge in case the new string isn't complete enough to load a font */ + pango_font_description_merge (font_desc, new_font_desc, TRUE); + pango_font_description_free (new_font_desc); + break; + } + + /* fall-through */ + } +#endif + default: + g_value_copy (value, prop_value); + break; + } + + /* Postprocessing */ + switch (prop_id) + { + case PROP_NAME: { + const char *name = g_value_get_string (value); + + g_assert (name != NULL); + priv->profile_dir = mateconf_concat_dir_and_key (CONF_PROFILES_PREFIX, name); + + mateconf_client_add_dir (priv->conf, priv->profile_dir, + MATECONF_CLIENT_PRELOAD_ONELEVEL, + NULL); + priv->notify_id = + mateconf_client_notify_add (priv->conf, + priv->profile_dir, + terminal_profile_mateconf_notify_cb, + profile, NULL, + NULL); + + break; + } + + case PROP_BACKGROUND_IMAGE_FILE: + /* Clear the cached image */ + g_value_set_object (g_value_array_get_nth (priv->properties, PROP_BACKGROUND_IMAGE), NULL); + priv->background_load_failed = FALSE; + g_object_notify (object, TERMINAL_PROFILE_BACKGROUND_IMAGE); + break; + + default: + break; + } +} + +static void +terminal_profile_notify (GObject *object, + GParamSpec *pspec) +{ + TerminalProfilePrivate *priv = TERMINAL_PROFILE (object)->priv; + void (* notify) (GObject *, GParamSpec *) = G_OBJECT_CLASS (terminal_profile_parent_class)->notify; + + _terminal_debug_print (TERMINAL_DEBUG_PROFILE, + "Property notification for prop %s\n", + pspec->name); + + if (notify) + notify (object, pspec); + + if (pspec->owner_type == TERMINAL_TYPE_PROFILE && + (pspec->flags & G_PARAM_WRITABLE) && + g_param_spec_get_qdata (pspec, mateconf_key_quark) != NULL && + pspec != priv->mateconf_notification_pspec) + terminal_profile_schedule_save (TERMINAL_PROFILE (object), pspec); +} + +static void +terminal_profile_class_init (TerminalProfileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + mateconf_key_quark = g_quark_from_static_string ("GT::MateConfKey"); + + g_type_class_add_private (object_class, sizeof (TerminalProfilePrivate)); + + object_class->constructor = terminal_profile_constructor; + object_class->finalize = terminal_profile_finalize; + object_class->get_property = terminal_profile_get_property; + object_class->set_property = terminal_profile_set_property; + object_class->notify = terminal_profile_notify; + + signals[FORGOTTEN] = + g_signal_new ("forgotten", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (TerminalProfileClass, forgotten), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* mateconf_key -> pspec hash */ + klass->mateconf_keys = g_hash_table_new (g_str_hash, g_str_equal); + +#define TERMINAL_PROFILE_PSPEC_STATIC (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB) + +#define TERMINAL_PROFILE_PROPERTY(propId, propSpec, propMateConf) \ +{\ + GParamSpec *pspec = propSpec;\ + g_object_class_install_property (object_class, propId, pspec);\ +\ + if (propMateConf)\ + {\ + g_param_spec_set_qdata (pspec, mateconf_key_quark, (gpointer) propMateConf);\ + g_hash_table_insert (klass->mateconf_keys, (gpointer) propMateConf, pspec);\ + }\ +} + +#define TERMINAL_PROFILE_PROPERTY_BOOLEAN(prop, propDefault, propMateConf) \ + TERMINAL_PROFILE_PROPERTY (PROP_##prop,\ + g_param_spec_boolean (TERMINAL_PROFILE_##prop, NULL, NULL,\ + propDefault,\ + G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\ + propMateConf) + +#define TERMINAL_PROFILE_PROPERTY_BOXED(prop, propType, propMateConf)\ + TERMINAL_PROFILE_PROPERTY (PROP_##prop,\ + g_param_spec_boxed (TERMINAL_PROFILE_##prop, NULL, NULL,\ + propType,\ + G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\ + propMateConf) + +#define TERMINAL_PROFILE_PROPERTY_DOUBLE(prop, propMin, propMax, propDefault, propMateConf)\ + TERMINAL_PROFILE_PROPERTY (PROP_##prop,\ + g_param_spec_double (TERMINAL_PROFILE_##prop, NULL, NULL,\ + propMin, propMax, propDefault,\ + G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\ + propMateConf) + +#define TERMINAL_PROFILE_PROPERTY_ENUM(prop, propType, propDefault, propMateConf)\ + TERMINAL_PROFILE_PROPERTY (PROP_##prop,\ + g_param_spec_enum (TERMINAL_PROFILE_##prop, NULL, NULL,\ + propType, propDefault,\ + G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\ + propMateConf) + +#define TERMINAL_PROFILE_PROPERTY_INT(prop, propMin, propMax, propDefault, propMateConf)\ + TERMINAL_PROFILE_PROPERTY (PROP_##prop,\ + g_param_spec_int (TERMINAL_PROFILE_##prop, NULL, NULL,\ + propMin, propMax, propDefault,\ + G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\ + propMateConf) + +/* these are all read-only */ +#define TERMINAL_PROFILE_PROPERTY_OBJECT(prop, propType, propMateConf)\ + TERMINAL_PROFILE_PROPERTY (PROP_##prop,\ + g_param_spec_object (TERMINAL_PROFILE_##prop, NULL, NULL,\ + propType,\ + G_PARAM_READABLE | TERMINAL_PROFILE_PSPEC_STATIC),\ + propMateConf) + +#define TERMINAL_PROFILE_PROPERTY_STRING(prop, propDefault, propMateConf)\ + TERMINAL_PROFILE_PROPERTY (PROP_##prop,\ + g_param_spec_string (TERMINAL_PROFILE_##prop, NULL, NULL,\ + propDefault,\ + G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\ + propMateConf) + +#define TERMINAL_PROFILE_PROPERTY_STRING_CO(prop, propDefault, propMateConf)\ + TERMINAL_PROFILE_PROPERTY (PROP_##prop,\ + g_param_spec_string (TERMINAL_PROFILE_##prop, NULL, NULL,\ + propDefault,\ + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | TERMINAL_PROFILE_PSPEC_STATIC),\ + propMateConf) + +#define TERMINAL_PROFILE_PROPERTY_VALUE_ARRAY_BOXED(prop, propElementName, propElementType, propMateConf)\ + TERMINAL_PROFILE_PROPERTY (PROP_##prop,\ + g_param_spec_value_array (TERMINAL_PROFILE_##prop, NULL, NULL,\ + g_param_spec_boxed (propElementName, NULL, NULL,\ + propElementType, \ + G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\ + G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\ + propMateConf) + + TERMINAL_PROFILE_PROPERTY_BOOLEAN (ALLOW_BOLD, DEFAULT_ALLOW_BOLD, KEY_ALLOW_BOLD); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (BOLD_COLOR_SAME_AS_FG, DEFAULT_BOLD_COLOR_SAME_AS_FG, KEY_BOLD_COLOR_SAME_AS_FG); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (DEFAULT_SHOW_MENUBAR, DEFAULT_DEFAULT_SHOW_MENUBAR, KEY_DEFAULT_SHOW_MENUBAR); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (LOGIN_SHELL, DEFAULT_LOGIN_SHELL, KEY_LOGIN_SHELL); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (SCROLL_BACKGROUND, DEFAULT_SCROLL_BACKGROUND, KEY_SCROLL_BACKGROUND); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (SCROLLBACK_UNLIMITED, DEFAULT_SCROLLBACK_UNLIMITED, KEY_SCROLLBACK_UNLIMITED); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (SCROLL_ON_KEYSTROKE, DEFAULT_SCROLL_ON_KEYSTROKE, KEY_SCROLL_ON_KEYSTROKE); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (SCROLL_ON_OUTPUT, DEFAULT_SCROLL_ON_OUTPUT, KEY_SCROLL_ON_OUTPUT); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (SILENT_BELL, DEFAULT_SILENT_BELL, KEY_SILENT_BELL); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (UPDATE_RECORDS, DEFAULT_UPDATE_RECORDS, KEY_UPDATE_RECORDS); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_CUSTOM_COMMAND, DEFAULT_USE_CUSTOM_COMMAND, KEY_USE_CUSTOM_COMMAND); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_CUSTOM_DEFAULT_SIZE, DEFAULT_USE_CUSTOM_DEFAULT_SIZE, KEY_USE_CUSTOM_DEFAULT_SIZE); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_SKEY, DEFAULT_USE_SKEY, KEY_USE_SKEY); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_SYSTEM_FONT, DEFAULT_USE_SYSTEM_FONT, KEY_USE_SYSTEM_FONT); + TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_THEME_COLORS, DEFAULT_USE_THEME_COLORS, KEY_USE_THEME_COLORS); + + TERMINAL_PROFILE_PROPERTY_BOXED (BACKGROUND_COLOR, GDK_TYPE_COLOR, KEY_BACKGROUND_COLOR); + TERMINAL_PROFILE_PROPERTY_BOXED (BOLD_COLOR, GDK_TYPE_COLOR, KEY_BOLD_COLOR); + TERMINAL_PROFILE_PROPERTY_BOXED (FONT, PANGO_TYPE_FONT_DESCRIPTION, KEY_FONT); + TERMINAL_PROFILE_PROPERTY_BOXED (FOREGROUND_COLOR, GDK_TYPE_COLOR, KEY_FOREGROUND_COLOR); + + /* 0.0 = normal bg, 1.0 = all black bg, 0.5 = half darkened */ + TERMINAL_PROFILE_PROPERTY_DOUBLE (BACKGROUND_DARKNESS, 0.0, 1.0, DEFAULT_BACKGROUND_DARKNESS, KEY_BACKGROUND_DARKNESS); + + TERMINAL_PROFILE_PROPERTY_ENUM (BACKGROUND_TYPE, TERMINAL_TYPE_BACKGROUND_TYPE, DEFAULT_BACKGROUND_TYPE, KEY_BACKGROUND_TYPE); + TERMINAL_PROFILE_PROPERTY_ENUM (BACKSPACE_BINDING, VTE_TYPE_TERMINAL_ERASE_BINDING, DEFAULT_BACKSPACE_BINDING, KEY_BACKSPACE_BINDING); + TERMINAL_PROFILE_PROPERTY_ENUM (CURSOR_BLINK_MODE, VTE_TYPE_TERMINAL_CURSOR_BLINK_MODE, DEFAULT_CURSOR_BLINK_MODE, KEY_CURSOR_BLINK_MODE); + TERMINAL_PROFILE_PROPERTY_ENUM (CURSOR_SHAPE, VTE_TYPE_TERMINAL_CURSOR_SHAPE, DEFAULT_CURSOR_SHAPE, KEY_CURSOR_SHAPE); + TERMINAL_PROFILE_PROPERTY_ENUM (DELETE_BINDING, VTE_TYPE_TERMINAL_ERASE_BINDING, DEFAULT_DELETE_BINDING, KEY_DELETE_BINDING); + TERMINAL_PROFILE_PROPERTY_ENUM (EXIT_ACTION, TERMINAL_TYPE_EXIT_ACTION, DEFAULT_EXIT_ACTION, KEY_EXIT_ACTION); + TERMINAL_PROFILE_PROPERTY_ENUM (SCROLLBAR_POSITION, TERMINAL_TYPE_SCROLLBAR_POSITION, DEFAULT_SCROLLBAR_POSITION, KEY_SCROLLBAR_POSITION); + TERMINAL_PROFILE_PROPERTY_ENUM (TITLE_MODE, TERMINAL_TYPE_TITLE_MODE, DEFAULT_TITLE_MODE, KEY_TITLE_MODE); + + TERMINAL_PROFILE_PROPERTY_INT (DEFAULT_SIZE_COLUMNS, 1, 1024, DEFAULT_DEFAULT_SIZE_COLUMNS, KEY_DEFAULT_SIZE_COLUMNS); + TERMINAL_PROFILE_PROPERTY_INT (DEFAULT_SIZE_ROWS, 1, 1024, DEFAULT_DEFAULT_SIZE_ROWS, KEY_DEFAULT_SIZE_ROWS); + TERMINAL_PROFILE_PROPERTY_INT (SCROLLBACK_LINES, 1, G_MAXINT, DEFAULT_SCROLLBACK_LINES, KEY_SCROLLBACK_LINES); + + TERMINAL_PROFILE_PROPERTY_OBJECT (BACKGROUND_IMAGE, GDK_TYPE_PIXBUF, NULL); + + TERMINAL_PROFILE_PROPERTY_STRING_CO (NAME, DEFAULT_NAME, NULL); + TERMINAL_PROFILE_PROPERTY_STRING (BACKGROUND_IMAGE_FILE, DEFAULT_BACKGROUND_IMAGE_FILE, KEY_BACKGROUND_IMAGE_FILE); + TERMINAL_PROFILE_PROPERTY_STRING (CUSTOM_COMMAND, DEFAULT_CUSTOM_COMMAND, KEY_CUSTOM_COMMAND); + TERMINAL_PROFILE_PROPERTY_STRING (TITLE, _(DEFAULT_TITLE), KEY_TITLE); + TERMINAL_PROFILE_PROPERTY_STRING (VISIBLE_NAME, _(DEFAULT_VISIBLE_NAME), KEY_VISIBLE_NAME); + TERMINAL_PROFILE_PROPERTY_STRING (WORD_CHARS, DEFAULT_WORD_CHARS, KEY_WORD_CHARS); + + TERMINAL_PROFILE_PROPERTY_VALUE_ARRAY_BOXED (PALETTE, "palette-color", GDK_TYPE_COLOR, KEY_PALETTE); +} + +/* Semi-Public API */ + +TerminalProfile* +_terminal_profile_new (const char *name) +{ + return g_object_new (TERMINAL_TYPE_PROFILE, + "name", name, + NULL); +} + +void +_terminal_profile_forget (TerminalProfile *profile) +{ + TerminalProfilePrivate *priv = profile->priv; + + if (!priv->forgotten) + { + mateconf_client_remove_dir (priv->conf, + priv->profile_dir, + NULL); + + priv->forgotten = TRUE; + + g_signal_emit (G_OBJECT (profile), signals[FORGOTTEN], 0); + } +} + +gboolean +_terminal_profile_get_forgotten (TerminalProfile *profile) +{ + return profile->priv->forgotten; +} + +TerminalProfile * +_terminal_profile_clone (TerminalProfile *base_profile, + const char *visible_name) +{ + TerminalApp *app = terminal_app_get (); + GObject *base_object = G_OBJECT (base_profile); + TerminalProfilePrivate *new_priv; + char profile_name[32]; + GParameter *params; + GParamSpec **pspecs; + guint n_pspecs, i, n_params, profile_num; + TerminalProfile *new_profile; + + g_object_ref (base_profile); + + profile_num = 0; + do + { + g_snprintf (profile_name, sizeof (profile_name), "Profile%u", profile_num++); + } + while (terminal_app_get_profile_by_name (app, profile_name) != NULL); + + /* Now we have an unused profile name */ + pspecs = g_object_class_list_properties (G_OBJECT_CLASS (TERMINAL_PROFILE_GET_CLASS (base_profile)), &n_pspecs); + + params = g_newa (GParameter, n_pspecs); + n_params = 0; + + for (i = 0; i < n_pspecs; ++i) + { + GParamSpec *pspec = pspecs[i]; + GValue *value; + + if (pspec->owner_type != TERMINAL_TYPE_PROFILE || + (pspec->flags & G_PARAM_WRITABLE) == 0) + continue; + + params[n_params].name = pspec->name; + + value = ¶ms[n_params].value; + G_VALUE_TYPE (value) = 0; + g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + + if (pspec->name == I_(TERMINAL_PROFILE_NAME)) + g_value_set_static_string (value, profile_name); + else if (pspec->name == I_(TERMINAL_PROFILE_VISIBLE_NAME)) + g_value_set_static_string (value, visible_name); + else + g_object_get_property (base_object, pspec->name, value); + + ++n_params; + } + + new_profile = g_object_newv (TERMINAL_TYPE_PROFILE, n_params, params); + + g_object_unref (base_profile); + + for (i = 0; i < n_params; ++i) + g_value_unset (¶ms[i].value); + + /* Flush the new profile to mateconf */ + new_priv = new_profile->priv; + + g_slist_free (new_priv->dirty_pspecs); + new_priv->dirty_pspecs = NULL; + if (new_priv->save_idle_id != 0) + { + g_source_remove (new_priv->save_idle_id); + new_priv->save_idle_id = 0; + } + + for (i = 0; i < n_pspecs; ++i) + { + GParamSpec *pspec = pspecs[i]; + + if (pspec->owner_type != TERMINAL_TYPE_PROFILE || + (pspec->flags & G_PARAM_WRITABLE) == 0) + continue; + + new_priv->dirty_pspecs = g_slist_prepend (new_priv->dirty_pspecs, pspec); + } + g_free (pspecs); + + terminal_profile_save (new_profile); + + return new_profile; +} + +/* Public API */ + +gboolean +terminal_profile_get_property_boolean (TerminalProfile *profile, + const char *prop_name) +{ + const GValue *value; + + value = get_prop_value_from_prop_name (profile, prop_name); + g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_BOOLEAN (value), FALSE); + if (!value || !G_VALUE_HOLDS_BOOLEAN (value)) + return FALSE; + + return g_value_get_boolean (value); +} + +gconstpointer +terminal_profile_get_property_boxed (TerminalProfile *profile, + const char *prop_name) +{ + const GValue *value; + + value = get_prop_value_from_prop_name (profile, prop_name); + g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_BOXED (value), NULL); + if (!value || !G_VALUE_HOLDS_BOXED (value)) + return NULL; + + return g_value_get_boxed (value); +} + +double +terminal_profile_get_property_double (TerminalProfile *profile, + const char *prop_name) +{ + const GValue *value; + + value = get_prop_value_from_prop_name (profile, prop_name); + g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_DOUBLE (value), 0.0); + if (!value || !G_VALUE_HOLDS_DOUBLE (value)) + return 0.0; + + return g_value_get_double (value); +} + +int +terminal_profile_get_property_enum (TerminalProfile *profile, + const char *prop_name) +{ + const GValue *value; + + value = get_prop_value_from_prop_name (profile, prop_name); + g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_ENUM (value), 0); + if (!value || !G_VALUE_HOLDS_ENUM (value)) + return 0; + + return g_value_get_enum (value); +} + +int +terminal_profile_get_property_int (TerminalProfile *profile, + const char *prop_name) +{ + const GValue *value; + + value = get_prop_value_from_prop_name (profile, prop_name); + g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_INT (value), 0); + if (!value || !G_VALUE_HOLDS_INT (value)) + return 0; + + return g_value_get_int (value); +} + +gpointer +terminal_profile_get_property_object (TerminalProfile *profile, + const char *prop_name) +{ + const GValue *value; + + value = get_prop_value_from_prop_name (profile, prop_name); + g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_OBJECT (value), NULL); + if (!value || !G_VALUE_HOLDS_OBJECT (value)) + return NULL; + + return g_value_get_object (value); +} + +const char* +terminal_profile_get_property_string (TerminalProfile *profile, + const char *prop_name) +{ + const GValue *value; + + value = get_prop_value_from_prop_name (profile, prop_name); + g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_STRING (value), NULL); + if (!value || !G_VALUE_HOLDS_STRING (value)) + return NULL; + + return g_value_get_string (value); +} + +gboolean +terminal_profile_property_locked (TerminalProfile *profile, + const char *prop_name) +{ + TerminalProfilePrivate *priv = profile->priv; + GParamSpec *pspec; + + pspec = get_pspec_from_name (profile, prop_name); + g_return_val_if_fail (pspec != NULL, FALSE); + if (!pspec) + return FALSE; + + return priv->locked[pspec->param_id]; +} + +void +terminal_profile_reset_property (TerminalProfile *profile, + const char *prop_name) +{ + GParamSpec *pspec; + + pspec = get_pspec_from_name (profile, prop_name); + g_return_if_fail (pspec != NULL); + if (!pspec || + (pspec->flags & G_PARAM_WRITABLE) == 0) + return; + + terminal_profile_reset_property_internal (profile, pspec, TRUE); +} + +gboolean +terminal_profile_get_palette (TerminalProfile *profile, + GdkColor *colors, + guint *n_colors) +{ + TerminalProfilePrivate *priv; + GValueArray *array; + guint i, n; + + g_return_val_if_fail (TERMINAL_IS_PROFILE (profile), FALSE); + g_return_val_if_fail (colors != NULL && n_colors != NULL, FALSE); + + priv = profile->priv; + array = g_value_get_boxed (g_value_array_get_nth (priv->properties, PROP_PALETTE)); + if (!array) + return FALSE; + + n = MIN (array->n_values, *n_colors); + for (i = 0; i < n; ++i) + { + GdkColor *color = g_value_get_boxed (g_value_array_get_nth (array, i)); + if (!color) + continue; /* shouldn't happen!! */ + + colors[i] = *color; + } + + *n_colors = n; + return TRUE; +} + +gboolean +terminal_profile_get_palette_is_builtin (TerminalProfile *profile, + guint *n) +{ + GdkColor colors[TERMINAL_PALETTE_SIZE]; + guint n_colors; + guint i; + + n_colors = G_N_ELEMENTS (colors); + if (!terminal_profile_get_palette (profile, colors, &n_colors) || + n_colors != TERMINAL_PALETTE_SIZE) + return FALSE; + + for (i = 0; i < TERMINAL_PALETTE_N_BUILTINS; ++i) + if (palette_cmp (colors, terminal_palettes[i])) + { + *n = i; + return TRUE; + } + + return FALSE; +} + +void +terminal_profile_set_palette_builtin (TerminalProfile *profile, + guint n) +{ + GValue value = { 0, }; + + g_return_if_fail (n < TERMINAL_PALETTE_N_BUILTINS); + + g_value_init (&value, G_TYPE_VALUE_ARRAY); + set_value_from_palette (&value, terminal_palettes[n], TERMINAL_PALETTE_SIZE); + g_object_set_property (G_OBJECT (profile), TERMINAL_PROFILE_PALETTE, &value); + g_value_unset (&value); +} + +gboolean +terminal_profile_modify_palette_entry (TerminalProfile *profile, + guint i, + const GdkColor *color) +{ + TerminalProfilePrivate *priv = profile->priv; + GValueArray *array; + GValue *value; + GdkColor *old_color; + + array = g_value_get_boxed (g_value_array_get_nth (priv->properties, PROP_PALETTE)); + if (!array || + i >= array->n_values) + return FALSE; + + value = g_value_array_get_nth (array, i); + old_color = g_value_get_boxed (value); + if (!old_color || + !gdk_color_equal (old_color, color)) + { + g_value_set_boxed (value, color); + g_object_notify (G_OBJECT (profile), TERMINAL_PROFILE_PALETTE); + } + + return TRUE; +} diff --git a/src/terminal-profile.h b/src/terminal-profile.h new file mode 100644 index 0000000..4275e53 --- /dev/null +++ b/src/terminal-profile.h @@ -0,0 +1,191 @@ +/* + * Copyright © 2001 Havoc Pennington + * Copyright © 2002 Mathias Hasselmann + * Copyright © 2008 Christian Persch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TERMINAL_PROFILE_H +#define TERMINAL_PROFILE_H + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +typedef enum +{ + /* this has to be kept in sync with the option menu in the profile editor UI file */ + TERMINAL_TITLE_REPLACE, + TERMINAL_TITLE_BEFORE, + TERMINAL_TITLE_AFTER, + TERMINAL_TITLE_IGNORE +} TerminalTitleMode; + +typedef enum +{ + TERMINAL_SCROLLBAR_LEFT, + TERMINAL_SCROLLBAR_RIGHT, + TERMINAL_SCROLLBAR_HIDDEN +} TerminalScrollbarPosition; + +typedef enum +{ + TERMINAL_EXIT_CLOSE, + TERMINAL_EXIT_RESTART, + TERMINAL_EXIT_HOLD +} TerminalExitAction; + +typedef enum +{ + TERMINAL_BACKGROUND_SOLID, + TERMINAL_BACKGROUND_IMAGE, + TERMINAL_BACKGROUND_TRANSPARENT +} TerminalBackgroundType; + +#define TERMINAL_PALETTE_SIZE 16 + +#define TERMINAL_PALETTE_TANGO 0 +#define TERMINAL_PALETTE_LINUX 1 +#define TERMINAL_PALETTE_XTERM 2 +#define TERMINAL_PALETTE_RXVT 3 +#define TERMINAL_PALETTE_N_BUILTINS 4 + +/* Property names */ +#define TERMINAL_PROFILE_ALLOW_BOLD "allow-bold" +#define TERMINAL_PROFILE_BACKGROUND_COLOR "background-color" +#define TERMINAL_PROFILE_BACKGROUND_DARKNESS "background-darkness" +#define TERMINAL_PROFILE_BACKGROUND_IMAGE "background-image" +#define TERMINAL_PROFILE_BACKGROUND_IMAGE_FILE "background-image-file" +#define TERMINAL_PROFILE_BACKGROUND_TYPE "background-type" +#define TERMINAL_PROFILE_BACKSPACE_BINDING "backspace-binding" +#define TERMINAL_PROFILE_BOLD_COLOR "bold-color" +#define TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG "bold-color-same-as-fg" +#define TERMINAL_PROFILE_CURSOR_BLINK_MODE "cursor-blink-mode" +#define TERMINAL_PROFILE_CURSOR_SHAPE "cursor-shape" +#define TERMINAL_PROFILE_CUSTOM_COMMAND "custom-command" +#define TERMINAL_PROFILE_DEFAULT_SHOW_MENUBAR "default-show-menubar" +#define TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS "default-size-columns" +#define TERMINAL_PROFILE_DEFAULT_SIZE_ROWS "default-size-rows" +#define TERMINAL_PROFILE_DELETE_BINDING "delete-binding" +#define TERMINAL_PROFILE_EXIT_ACTION "exit-action" +#define TERMINAL_PROFILE_FONT "font" +#define TERMINAL_PROFILE_FOREGROUND_COLOR "foreground-color" +#define TERMINAL_PROFILE_LOGIN_SHELL "login-shell" +#define TERMINAL_PROFILE_NAME "name" +#define TERMINAL_PROFILE_PALETTE "palette" +#define TERMINAL_PROFILE_SCROLL_BACKGROUND "scroll-background" +#define TERMINAL_PROFILE_SCROLLBACK_LINES "scrollback-lines" +#define TERMINAL_PROFILE_SCROLLBACK_UNLIMITED "scrollback-unlimited" +#define TERMINAL_PROFILE_SCROLLBAR_POSITION "scrollbar-position" +#define TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE "scroll-on-keystroke" +#define TERMINAL_PROFILE_SCROLL_ON_OUTPUT "scroll-on-output" +#define TERMINAL_PROFILE_SILENT_BELL "silent-bell" +#define TERMINAL_PROFILE_TITLE_MODE "title-mode" +#define TERMINAL_PROFILE_TITLE "title" +#define TERMINAL_PROFILE_UPDATE_RECORDS "update-records" +#define TERMINAL_PROFILE_USE_CUSTOM_COMMAND "use-custom-command" +#define TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE "use-custom-default-size" +#define TERMINAL_PROFILE_USE_SKEY "use-skey" +#define TERMINAL_PROFILE_USE_SYSTEM_FONT "use-system-font" +#define TERMINAL_PROFILE_USE_THEME_COLORS "use-theme-colors" +#define TERMINAL_PROFILE_VISIBLE_NAME "visible-name" +#define TERMINAL_PROFILE_WORD_CHARS "word-chars" + +/* TerminalProfile object */ + +#define TERMINAL_TYPE_PROFILE (terminal_profile_get_type ()) +#define TERMINAL_PROFILE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TERMINAL_TYPE_PROFILE, TerminalProfile)) +#define TERMINAL_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TERMINAL_TYPE_PROFILE, TerminalProfileClass)) +#define TERMINAL_IS_PROFILE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TERMINAL_TYPE_PROFILE)) +#define TERMINAL_IS_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TERMINAL_TYPE_PROFILE)) +#define TERMINAL_PROFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TERMINAL_TYPE_PROFILE, TerminalProfileClass)) + +typedef struct _TerminalProfile TerminalProfile; +typedef struct _TerminalProfileClass TerminalProfileClass; +typedef struct _TerminalProfilePrivate TerminalProfilePrivate; + +struct _TerminalProfile +{ + GObject parent_instance; + + TerminalProfilePrivate *priv; +}; + +struct _TerminalProfileClass +{ + GObjectClass parent_class; + + void (* forgotten) (TerminalProfile *profile); + + GHashTable *mateconf_keys; +}; + +GType terminal_profile_get_type (void); + +TerminalProfile* _terminal_profile_new (const char *name); + +void _terminal_profile_forget (TerminalProfile *profile); + +gboolean _terminal_profile_get_forgotten (TerminalProfile *profile); + +TerminalProfile* _terminal_profile_clone (TerminalProfile *base_profile, + const char *visible_name); + +gboolean terminal_profile_property_locked (TerminalProfile *profile, + const char *prop_name); + +void terminal_profile_reset_property (TerminalProfile *profile, + const char *prop_name); + +gboolean terminal_profile_get_property_boolean (TerminalProfile *profile, + const char *prop_name); + +gconstpointer terminal_profile_get_property_boxed (TerminalProfile *profile, + const char *prop_name); + +double terminal_profile_get_property_double (TerminalProfile *profile, + const char *prop_name); + +int terminal_profile_get_property_enum (TerminalProfile *profile, + const char *prop_name); + +int terminal_profile_get_property_int (TerminalProfile *profile, + const char *prop_name); + +gpointer terminal_profile_get_property_object (TerminalProfile *profile, + const char *prop_name); + +const char* terminal_profile_get_property_string (TerminalProfile *profile, + const char *prop_name); + +gboolean terminal_profile_get_palette (TerminalProfile *profile, + GdkColor *colors, + guint *n_colors); + +gboolean terminal_profile_get_palette_is_builtin (TerminalProfile *profile, + guint *n); + +void terminal_profile_set_palette_builtin (TerminalProfile *profile, + guint n); + +gboolean terminal_profile_modify_palette_entry (TerminalProfile *profile, + guint i, + const GdkColor *color); + +G_END_DECLS + +#endif /* TERMINAL_PROFILE_H */ diff --git a/src/terminal-screen-container.c b/src/terminal-screen-container.c new file mode 100644 index 0000000..7aa6265 --- /dev/null +++ b/src/terminal-screen-container.c @@ -0,0 +1,409 @@ +/* + * Copyright © 2008, 2010 Christian Persch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include "terminal-screen-container.h" +#include "terminal-debug.h" +#include "terminal-intl.h" + +#include <gtk/gtk.h> + +#define TERMINAL_SCREEN_CONTAINER_GET_PRIVATE(screen_container)(G_TYPE_INSTANCE_GET_PRIVATE ((screen_container), TERMINAL_TYPE_SCREEN_CONTAINER, TerminalScreenContainerPrivate)) + +struct _TerminalScreenContainerPrivate +{ + TerminalScreen *screen; +#ifdef USE_SCROLLED_WINDOW + GtkWidget *scrolled_window; +#else + GtkWidget *hbox; + GtkWidget *vscrollbar; +#endif + GtkPolicyType hscrollbar_policy; + GtkPolicyType vscrollbar_policy; + GtkCornerType window_placement; + guint window_placement_set : 1; +}; + +enum +{ + PROP_0, + PROP_SCREEN, + PROP_HSCROLLBAR_POLICY, + PROP_VSCROLLBAR_POLICY, + PROP_WINDOW_PLACEMENT, + PROP_WINDOW_PLACEMENT_SET +}; + +G_DEFINE_TYPE (TerminalScreenContainer, terminal_screen_container, GTK_TYPE_VBOX) + +/* helper functions */ + +static void +terminal_screen_container_set_placement_internal (TerminalScreenContainer *container, + GtkCornerType corner) +{ + TerminalScreenContainerPrivate *priv = container->priv; + +#ifdef USE_SCROLLED_WINDOW + gtk_scrolled_window_set_placement (GTK_SCROLLED_WINDOW (priv->scrolled_window), corner); +#else + switch (corner) { + case GTK_CORNER_TOP_LEFT: + case GTK_CORNER_BOTTOM_LEFT: + gtk_box_reorder_child (GTK_BOX (priv->hbox), priv->vscrollbar, 1); + break; + case GTK_CORNER_TOP_RIGHT: + case GTK_CORNER_BOTTOM_RIGHT: + gtk_box_reorder_child (GTK_BOX (priv->hbox), priv->vscrollbar, 0); + break; + default: + g_assert_not_reached (); + } +#endif + + priv->window_placement = corner; + g_object_notify (G_OBJECT (container), "window-placement"); +} + +static void +terminal_screen_container_set_placement_set (TerminalScreenContainer *container, + gboolean set) +{ + TerminalScreenContainerPrivate *priv = container->priv; + +#ifdef USE_SCROLLED_WINDOW + g_object_set (priv->scrolled_window, "window-placement-set", set, NULL); +#endif + + priv->window_placement_set = set != FALSE; + g_object_notify (G_OBJECT (container), "window-placement-set"); +} + +#if defined(USE_SCROLLED_WINDOW) && defined(MATE_ENABLE_DEBUG) +static void +size_request_cb (GtkWidget *widget, + GtkRequisition *req, + TerminalScreenContainer *container) +{ + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[screen %p] scrolled-window size req %d : %d\n", + container->priv->screen, req->width, req->height); +} +#endif + +/* Class implementation */ + +static void +terminal_screen_container_init (TerminalScreenContainer *container) +{ + TerminalScreenContainerPrivate *priv; + + priv = container->priv = TERMINAL_SCREEN_CONTAINER_GET_PRIVATE (container); + + priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC; + priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC; + priv->window_placement = GTK_CORNER_BOTTOM_RIGHT; + priv->window_placement_set = FALSE; +} + +static GObject * +terminal_screen_container_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) +{ + GObject *object; + TerminalScreenContainer *container; + TerminalScreenContainerPrivate *priv; + + object = G_OBJECT_CLASS (terminal_screen_container_parent_class)->constructor + (type, n_construct_properties, construct_params); + + container = TERMINAL_SCREEN_CONTAINER (object); + priv = container->priv; + + g_assert (priv->screen != NULL); + +#ifdef USE_SCROLLED_WINDOW + priv->scrolled_window = gtk_scrolled_window_new (NULL, vte_terminal_get_adjustment (VTE_TERMINAL (priv->screen))); + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window), + priv->hscrollbar_policy, + priv->vscrollbar_policy); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window), + GTK_SHADOW_NONE); + gtk_container_add (GTK_CONTAINER (priv->scrolled_window), GTK_WIDGET (priv->screen)); + gtk_widget_show (GTK_WIDGET (priv->screen)); + gtk_box_pack_end (GTK_BOX (container), priv->scrolled_window, TRUE, TRUE, 0); + gtk_widget_show (priv->scrolled_window); + +#ifdef MATE_ENABLE_DEBUG + g_signal_connect (priv->scrolled_window, "size-request", G_CALLBACK (size_request_cb), container); +#endif + +#else + + priv->hbox = gtk_hbox_new (FALSE, 0); + + priv->vscrollbar = gtk_vscrollbar_new (vte_terminal_get_adjustment (VTE_TERMINAL (priv->screen))); + + gtk_box_pack_start (GTK_BOX (priv->hbox), GTK_WIDGET (priv->screen), TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (priv->hbox), priv->vscrollbar, FALSE, FALSE, 0); + + gtk_box_pack_end (GTK_BOX (container), priv->hbox, TRUE, TRUE, 0); + gtk_widget_show_all (priv->hbox); +#endif /* USE_SCROLLED_WINDOW */ + + _terminal_screen_update_scrollbar (priv->screen); + + return object; +} + +static void +terminal_screen_container_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TerminalScreenContainer *container = TERMINAL_SCREEN_CONTAINER (object); + TerminalScreenContainerPrivate *priv = container->priv; + + switch (prop_id) { + case PROP_SCREEN: + break; + case PROP_HSCROLLBAR_POLICY: + g_value_set_enum (value, priv->hscrollbar_policy); + break; + case PROP_VSCROLLBAR_POLICY: + g_value_set_enum (value, priv->vscrollbar_policy); + break; + case PROP_WINDOW_PLACEMENT: + g_value_set_enum (value, priv->window_placement); + break; + case PROP_WINDOW_PLACEMENT_SET: + g_value_set_boolean (value, priv->window_placement_set); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_screen_container_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TerminalScreenContainer *container = TERMINAL_SCREEN_CONTAINER (object); + TerminalScreenContainerPrivate *priv = container->priv; + + switch (prop_id) { + case PROP_SCREEN: + priv->screen = g_value_get_object (value); + break; + case PROP_HSCROLLBAR_POLICY: + terminal_screen_container_set_policy (container, + g_value_get_enum (value), + priv->vscrollbar_policy); + break; + case PROP_VSCROLLBAR_POLICY: + terminal_screen_container_set_policy (container, + priv->hscrollbar_policy, + g_value_get_enum (value)); + break; + case PROP_WINDOW_PLACEMENT: + terminal_screen_container_set_placement_internal (container, g_value_get_enum (value)); + break; + case PROP_WINDOW_PLACEMENT_SET: + terminal_screen_container_set_placement_set (container, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_screen_container_class_init (TerminalScreenContainerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (gobject_class, sizeof (TerminalScreenContainerPrivate)); + + gobject_class->constructor = terminal_screen_container_constructor; + gobject_class->get_property = terminal_screen_container_get_property; + gobject_class->set_property = terminal_screen_container_set_property; + + g_object_class_install_property + (gobject_class, + PROP_SCREEN, + g_param_spec_object ("screen", NULL, NULL, + TERMINAL_TYPE_SCREEN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (gobject_class, + PROP_HSCROLLBAR_POLICY, + g_param_spec_enum ("hscrollbar-policy", NULL, NULL, + GTK_TYPE_POLICY_TYPE, + GTK_POLICY_AUTOMATIC, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (gobject_class, + PROP_VSCROLLBAR_POLICY, + g_param_spec_enum ("vscrollbar-policy", NULL, NULL, + GTK_TYPE_POLICY_TYPE, + GTK_POLICY_AUTOMATIC, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (gobject_class, + PROP_WINDOW_PLACEMENT, + g_param_spec_enum ("window-placement", NULL, NULL, + GTK_TYPE_CORNER_TYPE, + GTK_CORNER_TOP_LEFT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (gobject_class, + PROP_WINDOW_PLACEMENT_SET, + g_param_spec_boolean ("window-placement-set", NULL, NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} + +/* public API */ + +/** + * terminal_screen_container_new: + * @screen: a #TerminalScreen + * + * Returns: a new #TerminalScreenContainer for @screen + */ +GtkWidget * +terminal_screen_container_new (TerminalScreen *screen) +{ + return g_object_new (TERMINAL_TYPE_SCREEN_CONTAINER, + "screen", screen, + NULL); +} + +/** + * terminal_screen_container_get_screen: + * @container: a #TerminalScreenContainer + * + * Returns: @container's #TerminalScreen + */ +TerminalScreen * +terminal_screen_container_get_screen (TerminalScreenContainer *container) +{ + g_return_val_if_fail (TERMINAL_IS_SCREEN_CONTAINER (container), NULL); + + return container->priv->screen; +} + +/** + * terminal_screen_container_get_from_screen: + * @screen: a #TerminalScreenContainerPrivate + * + * Returns the #TerminalScreenContainer containing @screen. + */ +TerminalScreenContainer * +terminal_screen_container_get_from_screen (TerminalScreen *screen) +{ + g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), NULL); + + return TERMINAL_SCREEN_CONTAINER (gtk_widget_get_ancestor (GTK_WIDGET (screen), TERMINAL_TYPE_SCREEN_CONTAINER)); +} + +/** + * terminal_screen_container_set_policy: + * @container: a #TerminalScreenContainer + * @hpolicy: a #GtkPolicyType + * @vpolicy: a #GtkPolicyType + * + * Sets @container's scrollbar policy. + */ +void +terminal_screen_container_set_policy (TerminalScreenContainer *container, + GtkPolicyType hpolicy G_GNUC_UNUSED, + GtkPolicyType vpolicy) +{ + TerminalScreenContainerPrivate *priv; + GObject *object; + + g_return_if_fail (TERMINAL_IS_SCREEN_CONTAINER (container)); + + object = G_OBJECT (container); + priv = container->priv; + + g_object_freeze_notify (object); + + if (priv->hscrollbar_policy != hpolicy) { + priv->hscrollbar_policy = hpolicy; + g_object_notify (object, "hscrollbar-policy"); + } + if (priv->vscrollbar_policy != vpolicy) { + priv->vscrollbar_policy = vpolicy; + g_object_notify (object, "vscrollbar-policy"); + } + +#ifdef USE_SCROLLED_WINDOW + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window), hpolicy, vpolicy); +#else + switch (vpolicy) { + case GTK_POLICY_NEVER: + gtk_widget_hide (priv->vscrollbar); + break; + case GTK_POLICY_AUTOMATIC: + case GTK_POLICY_ALWAYS: + gtk_widget_show (priv->vscrollbar); + break; + default: + g_assert_not_reached (); + } +#endif + + g_object_thaw_notify (object); +} + +/** + * terminal_screen_container_set_placement: + * @container: a #TerminalScreenContainer + * @corner: a #GtkCornerType + * + * Sets @container's window placement. + */ +void +terminal_screen_container_set_placement (TerminalScreenContainer *container, + GtkCornerType corner) +{ + g_return_if_fail (TERMINAL_IS_SCREEN_CONTAINER (container)); + + terminal_screen_container_set_placement_internal (container, corner); + terminal_screen_container_set_placement_set (container, TRUE); +} diff --git a/src/terminal-screen-container.h b/src/terminal-screen-container.h new file mode 100644 index 0000000..31e4859 --- /dev/null +++ b/src/terminal-screen-container.h @@ -0,0 +1,69 @@ +/* + * Copyright © 2008, 2010 Christian Persch +* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TERMINAL_SCREEN_CONTAINER_H +#define TERMINAL_SCREEN_CONTAINER_H + +#include <gtk/gtk.h> +#include "terminal-screen.h" + +G_BEGIN_DECLS + +#define TERMINAL_TYPE_SCREEN_CONTAINER (terminal_screen_container_get_type ()) +#define TERMINAL_SCREEN_CONTAINER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TERMINAL_TYPE_SCREEN_CONTAINER, TerminalScreenContainer)) +#define TERMINAL_SCREEN_CONTAINER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TERMINAL_TYPE_SCREEN_CONTAINER, TerminalScreenContainerClass)) +#define TERMINAL_IS_SCREEN_CONTAINER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TERMINAL_TYPE_SCREEN_CONTAINER)) +#define TERMINAL_IS_SCREEN_CONTAINER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TERMINAL_TYPE_SCREEN_CONTAINER)) +#define TERMINAL_SCREEN_CONTAINER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TERMINAL_TYPE_SCREEN_CONTAINER, TerminalScreenContainerClass)) + +typedef struct _TerminalScreenContainer TerminalScreenContainer; +typedef struct _TerminalScreenContainerClass TerminalScreenContainerClass; +typedef struct _TerminalScreenContainerPrivate TerminalScreenContainerPrivate; + +struct _TerminalScreenContainer +{ + GtkVBox parent_instance; + + /*< private >*/ + TerminalScreenContainerPrivate *priv; +}; + +struct _TerminalScreenContainerClass +{ + GtkVBoxClass parent_class; +}; + +GType terminal_screen_container_get_type (void); + +GtkWidget *terminal_screen_container_new (TerminalScreen *screen); + +TerminalScreen *terminal_screen_container_get_screen (TerminalScreenContainer *container); + +TerminalScreenContainer *terminal_screen_container_get_from_screen (TerminalScreen *screen); + +void terminal_screen_container_set_policy (TerminalScreenContainer *container, + GtkPolicyType hpolicy, + GtkPolicyType vpolicy); + +void terminal_screen_container_set_placement (TerminalScreenContainer *container, + GtkCornerType corner); + +G_END_DECLS + +#endif /* TERMINAL_SCREEN_CONTAINER_H */ diff --git a/src/terminal-screen.c b/src/terminal-screen.c new file mode 100644 index 0000000..3899e30 --- /dev/null +++ b/src/terminal-screen.c @@ -0,0 +1,2343 @@ +/* + * Copyright © 2001 Havoc Pennington + * Copyright © 2007, 2008, 2010 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif + +#include "terminal-accels.h" +#include "terminal-app.h" +#include "terminal-debug.h" +#include "terminal-intl.h" +#include "terminal-marshal.h" +#include "terminal-profile.h" +#include "terminal-screen-container.h" +#include "terminal-util.h" +#include "terminal-window.h" +#include "terminal-info-bar.h" + +#include "eggshell.h" + +#define URL_MATCH_CURSOR (GDK_HAND2) +#define SKEY_MATCH_CURSOR (GDK_HAND2) + +typedef struct +{ + int tag; + TerminalURLFlavour flavor; +} TagData; + +struct _TerminalScreenPrivate +{ + TerminalProfile *profile; /* may be NULL at times */ + guint profile_changed_id; + guint profile_forgotten_id; + char *raw_title, *raw_icon_title; + char *cooked_title, *cooked_icon_title; + char *override_title; + gboolean icon_title_set; + char *initial_working_directory; + char **initial_env; + char **override_command; + int child_pid; + int pty_fd; + double font_scale; + gboolean user_title; /* title was manually set */ + GSList *match_tags; + guint launch_child_source_id; +}; + +enum +{ + PROFILE_SET, + SHOW_POPUP_MENU, + MATCH_CLICKED, + CLOSE_SCREEN, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_PROFILE, + PROP_ICON_TITLE, + PROP_ICON_TITLE_SET, + PROP_OVERRIDE_COMMAND, + PROP_TITLE, + PROP_INITIAL_ENVIRONMENT +}; + +enum +{ + TARGET_COLOR, + TARGET_BGIMAGE, + TARGET_RESET_BG, + TARGET_MOZ_URL, + TARGET_NETSCAPE_URL, + TARGET_TAB +}; + +static void terminal_screen_dispose (GObject *object); +static void terminal_screen_finalize (GObject *object); +static void terminal_screen_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time); +static void terminal_screen_system_font_notify_cb (TerminalApp *app, + GParamSpec *pspec, + TerminalScreen *screen); +static void terminal_screen_change_font (TerminalScreen *screen); +static gboolean terminal_screen_popup_menu (GtkWidget *widget); +static gboolean terminal_screen_button_press (GtkWidget *widget, + GdkEventButton *event); +static void terminal_screen_launch_child_on_idle (TerminalScreen *screen); +static void terminal_screen_child_exited (VteTerminal *terminal); + +static void terminal_screen_window_title_changed (VteTerminal *vte_terminal, + TerminalScreen *screen); +static void terminal_screen_icon_title_changed (VteTerminal *vte_terminal, + TerminalScreen *screen); + +static void update_color_scheme (TerminalScreen *screen); + +static gboolean terminal_screen_format_title (TerminalScreen *screen, const char *raw_title, char **old_cooked_title); + +static void terminal_screen_cook_title (TerminalScreen *screen); +static void terminal_screen_cook_icon_title (TerminalScreen *screen); + +static char* terminal_screen_check_match (TerminalScreen *screen, + int column, + int row, + int *flavor); + +static guint signals[LAST_SIGNAL]; + +#define USERCHARS "-[:alnum:]" +#define USERCHARS_CLASS "[" USERCHARS "]" +#define PASSCHARS_CLASS "[-[:alnum:]\\Q,?;.:/!%$^*&~\"#'\\E]" +#define HOSTCHARS_CLASS "[-[:alnum:]]" +#define HOST HOSTCHARS_CLASS "+(\\." HOSTCHARS_CLASS "+)*" +#define PORT "(?:\\:[[:digit:]]{1,5})?" +#define PATHCHARS_CLASS "[-[:alnum:]\\Q_$.+!*,;@&=?/~#%\\E]" +#define PATHTERM_CLASS "[^\\Q]'.}>) \t\r\n,\"\\E]" +#define SCHEME "(?:news:|telnet:|nntp:|file:\\/|https?:|ftps?:|sftp:|webcal:)" +#define USERPASS USERCHARS_CLASS "+(?:" PASSCHARS_CLASS "+)?" +#define URLPATH "(?:(/"PATHCHARS_CLASS"+(?:[(]"PATHCHARS_CLASS"*[)])*"PATHCHARS_CLASS"*)*"PATHTERM_CLASS")?" + +typedef struct { + const char *pattern; + TerminalURLFlavour flavor; + GRegexCompileFlags flags; +} TerminalRegexPattern; + +static const TerminalRegexPattern url_regex_patterns[] = { + { SCHEME "//(?:" USERPASS "\\@)?" HOST PORT URLPATH, FLAVOR_AS_IS, G_REGEX_CASELESS }, + { "(?:www|ftp)" HOSTCHARS_CLASS "*\\." HOST PORT URLPATH , FLAVOR_DEFAULT_TO_HTTP, G_REGEX_CASELESS }, + { "(?:callto:|h323:|sip:)" USERCHARS_CLASS "[" USERCHARS ".]*(?:" PORT "/[a-z0-9]+)?\\@" HOST, FLAVOR_VOIP_CALL, G_REGEX_CASELESS }, + { "(?:mailto:)?" USERCHARS_CLASS "[" USERCHARS ".]*\\@" HOSTCHARS_CLASS "+\\." HOST, FLAVOR_EMAIL, G_REGEX_CASELESS }, + { "news:[[:alnum:]\\Q^_{|}~!\"#$%&'()*+,./;:=?`\\E]+", FLAVOR_AS_IS, G_REGEX_CASELESS }, +}; + +static GRegex **url_regexes; +static TerminalURLFlavour *url_regex_flavors; +static guint n_url_regexes; + +#ifdef ENABLE_SKEY +static const TerminalRegexPattern skey_regex_patterns[] = { + { "s/key [[:digit:]]* [-[:alnum:]]*", FLAVOR_AS_IS }, + { "otp-[a-z0-9]* [[:digit:]]* [-[:alnum:]]*", FLAVOR_AS_IS }, +}; + +static GRegex **skey_regexes; +static guint n_skey_regexes; + +static void terminal_screen_skey_match_remove (TerminalScreen *screen); +#endif /* ENABLE_SKEY */ + +G_DEFINE_TYPE (TerminalScreen, terminal_screen, VTE_TYPE_TERMINAL) + +static char * +cwd_of_pid (int pid) +{ + static const char patterns[][18] = { + "/proc/%d/cwd", /* Linux */ + "/proc/%d/path/cwd", /* Solaris >= 10 */ + }; + guint i; + + if (pid == -1) + return NULL; + + /* Try to get the working directory using various OS-specific mechanisms */ + for (i = 0; i < G_N_ELEMENTS (patterns); ++i) + { + char cwd_file[64]; + char buf[PATH_MAX + 1]; + int len; + + g_snprintf (cwd_file, sizeof (cwd_file), patterns[i], pid); + len = readlink (cwd_file, buf, sizeof (buf) - 1); + + if (len > 0 && buf[0] == '/') + return g_strndup (buf, len); + + /* If that didn't do it, try this hack */ + if (len <= 0) + { + char *cwd, *working_dir = NULL; + + cwd = g_get_current_dir (); + if (cwd != NULL) + { + /* On Solaris, readlink returns an empty string, but the + * link can be used as a directory, including as a target + * of chdir(). + */ + if (chdir (cwd_file) == 0) + { + working_dir = g_get_current_dir (); + (void) chdir (cwd); + } + g_free (cwd); + } + + if (working_dir) + return working_dir; + } + } + + return NULL; +} + +static void +free_tag_data (TagData *tagdata) +{ + g_slice_free (TagData, tagdata); +} + +static void +terminal_screen_class_enable_menu_bar_accel_notify_cb (TerminalApp *app, + GParamSpec *pspec, + TerminalScreenClass *klass) +{ + static gboolean is_enabled = TRUE; /* the binding is enabled by default since GtkWidgetClass installs it */ + gboolean enable; + GtkBindingSet *binding_set; + + g_object_get (app, TERMINAL_APP_ENABLE_MENU_BAR_ACCEL, &enable, NULL); + + /* Only remove the 'skip' entry when we have added it previously! */ + if (enable == is_enabled) + return; + + is_enabled = enable; + + binding_set = gtk_binding_set_by_class (klass); + if (enable) + gtk_binding_entry_remove (binding_set, GDK_F10, GDK_SHIFT_MASK); + else + gtk_binding_entry_skip (binding_set, GDK_F10, GDK_SHIFT_MASK); +} + +static TerminalWindow * +terminal_screen_get_window (TerminalScreen *screen) +{ + GtkWidget *widget = GTK_WIDGET (screen); + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (widget); + if (!gtk_widget_is_toplevel (toplevel)) + return NULL; + + return TERMINAL_WINDOW (toplevel); +} + +static gboolean +window_uses_argb_visual (TerminalScreen *screen) +{ + TerminalWindow *window; + + window = terminal_screen_get_window (screen); + if (window == NULL || !gtk_widget_get_realized (GTK_WIDGET (window))) + return FALSE; + + return terminal_window_uses_argb_visual (window); +} + +static void +terminal_screen_realize (GtkWidget *widget) +{ + TerminalScreen *screen = TERMINAL_SCREEN (widget); + TerminalScreenPrivate *priv = screen->priv; + TerminalBackgroundType bg_type; + + GTK_WIDGET_CLASS (terminal_screen_parent_class)->realize (widget); + + /* FIXME: Don't enable this if we have a compmgr. */ + bg_type = terminal_profile_get_property_enum (priv->profile, TERMINAL_PROFILE_BACKGROUND_TYPE); + vte_terminal_set_background_transparent (VTE_TERMINAL (screen), + bg_type == TERMINAL_BACKGROUND_TRANSPARENT && + !window_uses_argb_visual (screen)); +} + +static void +terminal_screen_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + TerminalScreen *screen = TERMINAL_SCREEN (widget); + void (* style_set) (GtkWidget*, GtkStyle*) = GTK_WIDGET_CLASS (terminal_screen_parent_class)->style_set; + + if (style_set) + style_set (widget, previous_style); + + update_color_scheme (screen); + + if (gtk_widget_get_realized (widget)) + terminal_screen_change_font (screen); +} + +#ifdef MATE_ENABLE_DEBUG +static void +size_request (GtkWidget *widget, + GtkRequisition *req) +{ + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[screen %p] size-request %d : %d\n", + widget, req->width, req->height); +} + +static void +size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[screen %p] size-alloc %d : %d at (%d, %d)\n", + widget, allocation->width, allocation->height, allocation->x, allocation->y); +} +#endif + +static void +terminal_screen_init (TerminalScreen *screen) +{ + const GtkTargetEntry target_table[] = { + { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, TARGET_TAB }, + { "application/x-color", 0, TARGET_COLOR }, + { "property/bgimage", 0, TARGET_BGIMAGE }, + { "x-special/mate-reset-background", 0, TARGET_RESET_BG }, + { "text/x-moz-url", 0, TARGET_MOZ_URL }, + { "_NETSCAPE_URL", 0, TARGET_NETSCAPE_URL } + }; + VteTerminal *terminal = VTE_TERMINAL (screen); + TerminalScreenPrivate *priv; + GtkTargetList *target_list; + GtkTargetEntry *targets; + int n_targets; + guint i; + + priv = screen->priv = G_TYPE_INSTANCE_GET_PRIVATE (screen, TERMINAL_TYPE_SCREEN, TerminalScreenPrivate); + + vte_terminal_set_mouse_autohide (VTE_TERMINAL (screen), TRUE); + + priv->child_pid = -1; + priv->pty_fd = -1; + + priv->font_scale = PANGO_SCALE_MEDIUM; + + for (i = 0; i < n_url_regexes; ++i) + { + TagData *tag_data; + + tag_data = g_slice_new (TagData); + tag_data->flavor = url_regex_flavors[i]; + tag_data->tag = vte_terminal_match_add_gregex (terminal, url_regexes[i], 0); + vte_terminal_match_set_cursor_type (terminal, tag_data->tag, URL_MATCH_CURSOR); + + priv->match_tags = g_slist_prepend (priv->match_tags, tag_data); + } + + /* Setup DND */ + target_list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_uri_targets (target_list, 0); + gtk_target_list_add_text_targets (target_list, 0); + gtk_target_list_add_table (target_list, target_table, G_N_ELEMENTS (target_table)); + + targets = gtk_target_table_new_from_list (target_list, &n_targets); + + gtk_drag_dest_set (GTK_WIDGET (screen), + GTK_DEST_DEFAULT_MOTION | + GTK_DEST_DEFAULT_HIGHLIGHT | + GTK_DEST_DEFAULT_DROP, + targets, n_targets, + GDK_ACTION_COPY | GDK_ACTION_MOVE); + + gtk_target_table_free (targets, n_targets); + gtk_target_list_unref (target_list); + + priv->override_title = NULL; + priv->user_title = FALSE; + + g_signal_connect (screen, "window-title-changed", + G_CALLBACK (terminal_screen_window_title_changed), + screen); + g_signal_connect (screen, "icon-title-changed", + G_CALLBACK (terminal_screen_icon_title_changed), + screen); + + g_signal_connect (terminal_app_get (), "notify::system-font", + G_CALLBACK (terminal_screen_system_font_notify_cb), screen); + +#ifdef MATE_ENABLE_DEBUG + _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_GEOMETRY) + { + g_signal_connect_after (screen, "size-request", G_CALLBACK (size_request), NULL); + g_signal_connect_after (screen, "size-allocate", G_CALLBACK (size_allocate), NULL); + } +#endif +} + +static void +terminal_screen_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TerminalScreen *screen = TERMINAL_SCREEN (object); + + switch (prop_id) + { + case PROP_PROFILE: + g_value_set_object (value, terminal_screen_get_profile (screen)); + break; + case PROP_ICON_TITLE: + g_value_set_string (value, terminal_screen_get_icon_title (screen)); + break; + case PROP_ICON_TITLE_SET: + g_value_set_boolean (value, terminal_screen_get_icon_title_set (screen)); + break; + case PROP_OVERRIDE_COMMAND: + g_value_set_boxed (value, terminal_screen_get_override_command (screen)); + break; + case PROP_INITIAL_ENVIRONMENT: + g_value_set_boxed (value, terminal_screen_get_initial_environment (screen)); + break; + case PROP_TITLE: + g_value_set_string (value, terminal_screen_get_title (screen)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_screen_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TerminalScreen *screen = TERMINAL_SCREEN (object); + + switch (prop_id) + { + case PROP_PROFILE: { + TerminalProfile *profile; + + profile = g_value_get_object (value); + g_assert (profile != NULL); + terminal_screen_set_profile (screen, profile); + break; + } + case PROP_OVERRIDE_COMMAND: + terminal_screen_set_override_command (screen, g_value_get_boxed (value)); + break; + case PROP_INITIAL_ENVIRONMENT: + terminal_screen_set_initial_environment (screen, g_value_get_boxed (value)); + break; + case PROP_ICON_TITLE: + case PROP_ICON_TITLE_SET: + case PROP_TITLE: + /* not writable */ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_screen_class_init (TerminalScreenClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + VteTerminalClass *terminal_class = VTE_TERMINAL_CLASS (klass); + TerminalApp *app; + guint i; + + object_class->dispose = terminal_screen_dispose; + object_class->finalize = terminal_screen_finalize; + object_class->get_property = terminal_screen_get_property; + object_class->set_property = terminal_screen_set_property; + + widget_class->realize = terminal_screen_realize; + widget_class->style_set = terminal_screen_style_set; + widget_class->drag_data_received = terminal_screen_drag_data_received; + widget_class->button_press_event = terminal_screen_button_press; + widget_class->popup_menu = terminal_screen_popup_menu; + + terminal_class->child_exited = terminal_screen_child_exited; + + signals[PROFILE_SET] = + g_signal_new (I_("profile-set"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (TerminalScreenClass, profile_set), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, TERMINAL_TYPE_PROFILE); + + signals[SHOW_POPUP_MENU] = + g_signal_new (I_("show-popup-menu"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (TerminalScreenClass, show_popup_menu), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + + signals[MATCH_CLICKED] = + g_signal_new (I_("match-clicked"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (TerminalScreenClass, match_clicked), + g_signal_accumulator_true_handled, NULL, + _terminal_marshal_BOOLEAN__STRING_INT_UINT, + G_TYPE_BOOLEAN, + 3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_UINT); + + signals[CLOSE_SCREEN] = + g_signal_new (I_("close-screen"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (TerminalScreenClass, close_screen), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_object_class_install_property + (object_class, + PROP_PROFILE, + g_param_spec_string ("profile", NULL, NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property + (object_class, + PROP_ICON_TITLE, + g_param_spec_string ("icon-title", NULL, NULL, + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property + (object_class, + PROP_ICON_TITLE_SET, + g_param_spec_boolean ("icon-title-set", NULL, NULL, + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property + (object_class, + PROP_OVERRIDE_COMMAND, + g_param_spec_boxed ("override-command", NULL, NULL, + G_TYPE_STRV, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property + (object_class, + PROP_TITLE, + g_param_spec_string ("title", NULL, NULL, + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property + (object_class, + PROP_INITIAL_ENVIRONMENT, + g_param_spec_boxed ("initial-environment", NULL, NULL, + G_TYPE_STRV, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_type_class_add_private (object_class, sizeof (TerminalScreenPrivate)); + + /* Precompile the regexes */ + n_url_regexes = G_N_ELEMENTS (url_regex_patterns); + url_regexes = g_new0 (GRegex*, n_url_regexes); + url_regex_flavors = g_new0 (TerminalURLFlavour, n_url_regexes); + + for (i = 0; i < n_url_regexes; ++i) + { + GError *error = NULL; + + url_regexes[i] = g_regex_new (url_regex_patterns[i].pattern, + url_regex_patterns[i].flags | G_REGEX_OPTIMIZE, + 0, &error); + if (error) + { + g_message ("%s", error->message); + g_error_free (error); + } + + url_regex_flavors[i] = url_regex_patterns[i].flavor; + } + +#ifdef ENABLE_SKEY + n_skey_regexes = G_N_ELEMENTS (skey_regex_patterns); + skey_regexes = g_new0 (GRegex*, n_skey_regexes); + + for (i = 0; i < n_skey_regexes; ++i) + { + GError *error = NULL; + + skey_regexes[i] = g_regex_new (skey_regex_patterns[i].pattern, G_REGEX_OPTIMIZE, 0, &error); + if (error) + { + g_message ("%s", error->message); + g_error_free (error); + } + } +#endif /* ENABLE_SKEY */ + + /* This fixes bug #329827 */ + app = terminal_app_get (); + terminal_screen_class_enable_menu_bar_accel_notify_cb (app, NULL, klass); + g_signal_connect (app, "notify::" TERMINAL_APP_ENABLE_MENU_BAR_ACCEL, + G_CALLBACK (terminal_screen_class_enable_menu_bar_accel_notify_cb), klass); +} + +static void +terminal_screen_dispose (GObject *object) +{ + TerminalScreen *screen = TERMINAL_SCREEN (object); + TerminalScreenPrivate *priv = screen->priv; + GtkSettings *settings; + + settings = gtk_widget_get_settings (GTK_WIDGET (screen)); + g_signal_handlers_disconnect_matched (settings, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, + screen); + + if (priv->launch_child_source_id != 0) + { + g_source_remove (priv->launch_child_source_id); + priv->launch_child_source_id = 0; + } + + G_OBJECT_CLASS (terminal_screen_parent_class)->dispose (object); +} + +static void +terminal_screen_finalize (GObject *object) +{ + TerminalScreen *screen = TERMINAL_SCREEN (object); + TerminalScreenPrivate *priv = screen->priv; + + g_signal_handlers_disconnect_by_func (terminal_app_get (), + G_CALLBACK (terminal_screen_system_font_notify_cb), + screen); + + terminal_screen_set_profile (screen, NULL); + + g_free (priv->raw_title); + g_free (priv->cooked_title); + g_free (priv->override_title); + g_free (priv->raw_icon_title); + g_free (priv->cooked_icon_title); + g_free (priv->initial_working_directory); + g_strfreev (priv->override_command); + g_strfreev (priv->initial_env); + + g_slist_foreach (priv->match_tags, (GFunc) free_tag_data, NULL); + g_slist_free (priv->match_tags); + + G_OBJECT_CLASS (terminal_screen_parent_class)->finalize (object); +} + +TerminalScreen * +terminal_screen_new (TerminalProfile *profile, + char **override_command, + const char *title, + const char *working_dir, + char **child_env, + double zoom) +{ + TerminalScreen *screen; + TerminalScreenPrivate *priv; + + g_return_val_if_fail (TERMINAL_IS_PROFILE (profile), NULL); + + screen = g_object_new (TERMINAL_TYPE_SCREEN, NULL); + priv = screen->priv; + + terminal_screen_set_profile (screen, profile); + + if (terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE)) { + vte_terminal_set_size (VTE_TERMINAL (screen), + terminal_profile_get_property_int (profile, TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS), + terminal_profile_get_property_int (profile, TERMINAL_PROFILE_DEFAULT_SIZE_ROWS)); + } + + if (title) + terminal_screen_set_override_title (screen, title); + + priv->initial_working_directory = g_strdup (working_dir); + + if (override_command) + terminal_screen_set_override_command (screen, override_command); + + if (child_env) + terminal_screen_set_initial_environment (screen, child_env); + + terminal_screen_set_font_scale (screen, zoom); + terminal_screen_set_font (screen); + + /* Launch the child on idle */ + terminal_screen_launch_child_on_idle (screen); + + return screen; +} + +const char* +terminal_screen_get_raw_title (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + + if (priv->raw_title) + return priv->raw_title; + + return ""; +} + +const char* +terminal_screen_get_title (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + + if (priv->cooked_title == NULL) + terminal_screen_cook_title (screen); + + /* cooked_title may still be NULL */ + if (priv->cooked_title != NULL) + return priv->cooked_title; + else + return ""; +} + +const char* +terminal_screen_get_icon_title (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + + if (priv->cooked_icon_title == NULL) + terminal_screen_cook_icon_title (screen); + + /* cooked_icon_title may still be NULL */ + if (priv->cooked_icon_title != NULL) + return priv->cooked_icon_title; + else + return ""; +} + +gboolean +terminal_screen_get_icon_title_set (TerminalScreen *screen) +{ + return screen->priv->icon_title_set; +} + +/* Supported format specifiers: + * %S = static title + * %D = dynamic title + * %A = dynamic title, falling back to static title if empty + * %- = separator, if not at start or end of string (excluding whitespace) + */ +static const char * +terminal_screen_get_title_format (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + static const char *formats[] = { + "%A" /* TERMINAL_TITLE_REPLACE */, + "%D%-%S" /* TERMINAL_TITLE_BEFORE */, + "%S%-%D" /* TERMINAL_TITLE_AFTER */, + "%S" /* TERMINAL_TITLE_IGNORE */ + }; + + return formats[terminal_profile_get_property_enum (priv->profile, TERMINAL_PROFILE_TITLE_MODE)]; +} + +/** + * terminal_screen_format_title:: + * @screen: + * @raw_title: main ingredient + * @titleptr <inout>: pointer of the current title string + * + * Format title according @format, and stores it in <literal>*titleptr</literal>. + * Always ensures that *titleptr will be non-NULL. + * + * Returns: %TRUE iff the title changed + */ +static gboolean +terminal_screen_format_title (TerminalScreen *screen, + const char *raw_title, + char **titleptr) +{ + TerminalScreenPrivate *priv = screen->priv; + const char *format, *arg; + const char *static_title = NULL; + GString *title; + gboolean add_sep = FALSE; + + g_assert (titleptr); + + /* use --title argument if one was supplied, otherwise ask the profile */ + if (priv->override_title) + static_title = priv->override_title; + else + static_title = terminal_profile_get_property_string (priv->profile, TERMINAL_PROFILE_TITLE); + + //title = g_string_sized_new (strlen (static_title) + strlen (raw_title) + 3 + 1); + title = g_string_sized_new (128); + + format = terminal_screen_get_title_format (screen); + for (arg = format; *arg; arg += 2) + { + const char *text_to_append = NULL; + + g_assert (arg[0] == '%'); + + switch (arg[1]) + { + case 'A': + text_to_append = raw_title ? raw_title : static_title; + break; + case 'D': + text_to_append = raw_title; + break; + case 'S': + text_to_append = static_title; + break; + case '-': + text_to_append = NULL; + add_sep = TRUE; + break; + default: + g_assert_not_reached (); + } + + if (!text_to_append || !text_to_append[0]) + continue; + + if (add_sep && title->len > 0) + g_string_append (title, " - "); + + g_string_append (title, text_to_append); + add_sep = FALSE; + } + + if (*titleptr == NULL || strcmp (title->str, *titleptr) != 0) + { + g_free (*titleptr); + *titleptr = g_string_free (title, FALSE); + return TRUE; + } + + g_string_free (title, TRUE); + return FALSE; +} + +static void +terminal_screen_cook_title (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + + if (terminal_screen_format_title (screen, priv->raw_title, &priv->cooked_title)) + g_object_notify (G_OBJECT (screen), "title"); +} + +static void +terminal_screen_cook_icon_title (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + + if (terminal_screen_format_title (screen, priv->raw_icon_title, &priv->cooked_icon_title)) + g_object_notify (G_OBJECT (screen), "icon-title"); +} + +static void +terminal_screen_profile_notify_cb (TerminalProfile *profile, + GParamSpec *pspec, + TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + GObject *object = G_OBJECT (screen); + VteTerminal *vte_terminal = VTE_TERMINAL (screen); + const char *prop_name; + TerminalBackgroundType bg_type; + TerminalWindow *window; + + if (pspec) + prop_name = pspec->name; + else + prop_name = NULL; + + g_object_freeze_notify (object); + + if ((window = terminal_screen_get_window (screen))) + { + /* We need these in line for the set_size in + * update_on_realize + */ + terminal_window_update_geometry (window); + } + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLLBAR_POSITION)) + _terminal_screen_update_scrollbar (screen); + + if (!prop_name || + prop_name == I_(TERMINAL_PROFILE_TITLE_MODE) || + prop_name == I_(TERMINAL_PROFILE_TITLE)) + { + terminal_screen_cook_title (screen); + terminal_screen_cook_icon_title (screen); + } + + if (gtk_widget_get_realized (GTK_WIDGET (screen)) && + (!prop_name || + prop_name == I_(TERMINAL_PROFILE_USE_SYSTEM_FONT) || + prop_name == I_(TERMINAL_PROFILE_FONT))) + terminal_screen_change_font (screen); + + if (!prop_name || + prop_name == I_(TERMINAL_PROFILE_USE_THEME_COLORS) || + prop_name == I_(TERMINAL_PROFILE_FOREGROUND_COLOR) || + prop_name == I_(TERMINAL_PROFILE_BACKGROUND_COLOR) || + prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG) || + prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR) || + prop_name == I_(TERMINAL_PROFILE_PALETTE)) + update_color_scheme (screen); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SILENT_BELL)) + vte_terminal_set_audible_bell (vte_terminal, !terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_SILENT_BELL)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_WORD_CHARS)) + vte_terminal_set_word_chars (vte_terminal, + terminal_profile_get_property_string (profile, TERMINAL_PROFILE_WORD_CHARS)); + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE)) + vte_terminal_set_scroll_on_keystroke (vte_terminal, + terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE)); + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLL_ON_OUTPUT)) + vte_terminal_set_scroll_on_output (vte_terminal, + terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_SCROLL_ON_OUTPUT)); + if (!prop_name || + prop_name == I_(TERMINAL_PROFILE_SCROLLBACK_LINES) || + prop_name == I_(TERMINAL_PROFILE_SCROLLBACK_UNLIMITED)) + { + glong lines = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_SCROLLBACK_UNLIMITED) ? + -1 : terminal_profile_get_property_int (profile, TERMINAL_PROFILE_SCROLLBACK_LINES); + vte_terminal_set_scrollback_lines (vte_terminal, lines); + } + +#ifdef ENABLE_SKEY + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_USE_SKEY)) + { + if (terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_SKEY)) + { + guint i; + + for (i = 0; i < n_skey_regexes; ++i) + { + TagData *tag_data; + + tag_data = g_slice_new (TagData); + tag_data->flavor = FLAVOR_SKEY; + tag_data->tag = vte_terminal_match_add_gregex (vte_terminal, skey_regexes[i], 0); + vte_terminal_match_set_cursor_type (vte_terminal, tag_data->tag, SKEY_MATCH_CURSOR); + + priv->match_tags = g_slist_prepend (priv->match_tags, tag_data); + } + } + else + { + terminal_screen_skey_match_remove (screen); + } + } +#endif /* ENABLE_SKEY */ + + if (!prop_name || + prop_name == I_(TERMINAL_PROFILE_BACKGROUND_TYPE) || + prop_name == I_(TERMINAL_PROFILE_BACKGROUND_IMAGE) || + prop_name == I_(TERMINAL_PROFILE_BACKGROUND_DARKNESS) || + prop_name == I_(TERMINAL_PROFILE_SCROLL_BACKGROUND)) + { + bg_type = terminal_profile_get_property_enum (profile, TERMINAL_PROFILE_BACKGROUND_TYPE); + + if (bg_type == TERMINAL_BACKGROUND_IMAGE) + { + vte_terminal_set_background_image (vte_terminal, + terminal_profile_get_property_object (profile, TERMINAL_PROFILE_BACKGROUND_IMAGE)); + vte_terminal_set_scroll_background (vte_terminal, + terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_SCROLL_BACKGROUND)); + } + else + { + vte_terminal_set_background_image (vte_terminal, NULL); + vte_terminal_set_scroll_background (vte_terminal, FALSE); + } + + if (bg_type == TERMINAL_BACKGROUND_IMAGE || + bg_type == TERMINAL_BACKGROUND_TRANSPARENT) + { + vte_terminal_set_background_saturation (vte_terminal, + 1.0 - terminal_profile_get_property_double (profile, TERMINAL_PROFILE_BACKGROUND_DARKNESS)); + vte_terminal_set_opacity (vte_terminal, + 0xffff * terminal_profile_get_property_double (profile, TERMINAL_PROFILE_BACKGROUND_DARKNESS)); + } + else + { + vte_terminal_set_background_saturation (vte_terminal, 1.0); /* normal color */ + vte_terminal_set_opacity (vte_terminal, 0xffff); + } + + /* FIXME: Don't enable this if we have a compmgr. */ + vte_terminal_set_background_transparent (vte_terminal, + bg_type == TERMINAL_BACKGROUND_TRANSPARENT && + !window_uses_argb_visual (screen)); + } + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_BACKSPACE_BINDING)) + vte_terminal_set_backspace_binding (vte_terminal, + terminal_profile_get_property_enum (profile, TERMINAL_PROFILE_BACKSPACE_BINDING)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_DELETE_BINDING)) + vte_terminal_set_delete_binding (vte_terminal, + terminal_profile_get_property_enum (profile, TERMINAL_PROFILE_DELETE_BINDING)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_ALLOW_BOLD)) + vte_terminal_set_allow_bold (vte_terminal, + terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_ALLOW_BOLD)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_CURSOR_BLINK_MODE)) + vte_terminal_set_cursor_blink_mode (vte_terminal, + terminal_profile_get_property_enum (priv->profile, TERMINAL_PROFILE_CURSOR_BLINK_MODE)); + + if (!prop_name || prop_name == I_(TERMINAL_PROFILE_CURSOR_SHAPE)) + vte_terminal_set_cursor_shape (vte_terminal, + terminal_profile_get_property_enum (priv->profile, TERMINAL_PROFILE_CURSOR_SHAPE)); + + g_object_thaw_notify (object); +} + +static void +update_color_scheme (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + TerminalProfile *profile = priv->profile; + GtkStyle *style; + GdkColor colors[TERMINAL_PALETTE_SIZE]; + const GdkColor *fg_color, *bg_color, *bold_color; + GdkColor fg, bg; + guint n_colors; + + style = gtk_widget_get_style (GTK_WIDGET (screen)); + if (!style) + return; + + fg = style->text[GTK_STATE_NORMAL]; + bg = style->base[GTK_STATE_NORMAL]; + bold_color = NULL; + + if (!terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_THEME_COLORS)) + { + fg_color = terminal_profile_get_property_boxed (profile, TERMINAL_PROFILE_FOREGROUND_COLOR); + bg_color = terminal_profile_get_property_boxed (profile, TERMINAL_PROFILE_BACKGROUND_COLOR); + + if (!terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG)) + bold_color = terminal_profile_get_property_boxed (profile, TERMINAL_PROFILE_BOLD_COLOR); + + if (fg_color) + fg = *fg_color; + if (bg_color) + bg = *bg_color; + } + + n_colors = G_N_ELEMENTS (colors); + terminal_profile_get_palette (priv->profile, colors, &n_colors); + vte_terminal_set_colors (VTE_TERMINAL (screen), &fg, &bg, + colors, n_colors); + if (bold_color) + vte_terminal_set_color_bold (VTE_TERMINAL (screen), bold_color); + vte_terminal_set_background_tint_color (VTE_TERMINAL (screen), &bg); +} + +void +terminal_screen_set_font (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + TerminalProfile *profile; + PangoFontDescription *desc; + + profile = priv->profile; + + if (terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_SYSTEM_FONT)) + g_object_get (terminal_app_get (), "system-font", &desc, NULL); + else + g_object_get (profile, TERMINAL_PROFILE_FONT, &desc, NULL); + g_assert (desc); + + if (pango_font_description_get_size_is_absolute (desc)) + pango_font_description_set_absolute_size (desc, + priv->font_scale * + pango_font_description_get_size (desc)); + else + pango_font_description_set_size (desc, + priv->font_scale * + pango_font_description_get_size (desc)); + + vte_terminal_set_font (VTE_TERMINAL (screen), desc); + + pango_font_description_free (desc); +} + +static void +terminal_screen_system_font_notify_cb (TerminalApp *app, + GParamSpec *pspec, + TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + + if (!gtk_widget_get_realized (GTK_WIDGET (screen))) + return; + + if (!terminal_profile_get_property_boolean (priv->profile, TERMINAL_PROFILE_USE_SYSTEM_FONT)) + return; + + terminal_screen_change_font (screen); +} + +static void +terminal_screen_change_font (TerminalScreen *screen) +{ + TerminalWindow *window; + + terminal_screen_set_font (screen); + + window = terminal_screen_get_window (screen); + terminal_window_set_size (window, screen, TRUE); +} + +static void +profile_forgotten_callback (TerminalProfile *profile, + TerminalScreen *screen) +{ + TerminalProfile *new_profile; + + new_profile = terminal_app_get_profile_for_new_term (terminal_app_get ()); + g_assert (new_profile != NULL); + terminal_screen_set_profile (screen, new_profile); +} + +void +terminal_screen_set_profile (TerminalScreen *screen, + TerminalProfile *profile) +{ + TerminalScreenPrivate *priv = screen->priv; + TerminalProfile *old_profile; + + old_profile = priv->profile; + if (profile == old_profile) + return; + + if (priv->profile_changed_id) + { + g_signal_handler_disconnect (G_OBJECT (priv->profile), + priv->profile_changed_id); + priv->profile_changed_id = 0; + } + + if (priv->profile_forgotten_id) + { + g_signal_handler_disconnect (G_OBJECT (priv->profile), + priv->profile_forgotten_id); + priv->profile_forgotten_id = 0; + } + + priv->profile = profile; + if (profile) + { + g_object_ref (profile); + priv->profile_changed_id = + g_signal_connect (profile, "notify", + G_CALLBACK (terminal_screen_profile_notify_cb), + screen); + priv->profile_forgotten_id = + g_signal_connect (G_OBJECT (profile), + "forgotten", + G_CALLBACK (profile_forgotten_callback), + screen); + + terminal_screen_profile_notify_cb (profile, NULL, screen); + + g_signal_emit (G_OBJECT (screen), signals[PROFILE_SET], 0, old_profile); + } + + if (old_profile) + g_object_unref (old_profile); + + g_object_notify (G_OBJECT (screen), "profile"); +} + +TerminalProfile* +terminal_screen_get_profile (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + + g_assert (priv->profile != NULL); + return priv->profile; +} + +void +terminal_screen_set_override_command (TerminalScreen *screen, + char **argv) +{ + TerminalScreenPrivate *priv; + + g_return_if_fail (TERMINAL_IS_SCREEN (screen)); + + priv = screen->priv; + g_strfreev (priv->override_command); + priv->override_command = g_strdupv (argv); +} + +const char** +terminal_screen_get_override_command (TerminalScreen *screen) +{ + g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), NULL); + + return (const char**) screen->priv->override_command; +} + +void +terminal_screen_set_initial_environment (TerminalScreen *screen, + char **argv) +{ + TerminalScreenPrivate *priv; + + g_return_if_fail (TERMINAL_IS_SCREEN (screen)); + + priv = screen->priv; + g_assert (priv->initial_env == NULL); + priv->initial_env = g_strdupv (argv); +} + +char** +terminal_screen_get_initial_environment (TerminalScreen *screen) +{ + g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), NULL); + + return screen->priv->initial_env; +} + +static gboolean +get_child_command (TerminalScreen *screen, + const char *shell_env, + GSpawnFlags *spawn_flags_p, + char ***argv_p, + GError **err) +{ + TerminalScreenPrivate *priv = screen->priv; + TerminalProfile *profile; + char **argv; + + g_assert (spawn_flags_p != NULL && argv_p != NULL); + + profile = priv->profile; + + *argv_p = argv = NULL; + + if (priv->override_command) + { + argv = g_strdupv (priv->override_command); + + *spawn_flags_p |= G_SPAWN_SEARCH_PATH; + } + else if (terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_CUSTOM_COMMAND)) + { + if (!g_shell_parse_argv (terminal_profile_get_property_string (profile, TERMINAL_PROFILE_CUSTOM_COMMAND), + NULL, &argv, + err)) + return FALSE; + + *spawn_flags_p |= G_SPAWN_SEARCH_PATH; + } + else + { + const char *only_name; + char *shell; + int argc = 0; + + shell = egg_shell (shell_env); + + only_name = strrchr (shell, '/'); + if (only_name != NULL) + only_name++; + else + only_name = shell; + + argv = g_new (char*, 3); + + argv[argc++] = shell; + + if (terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_LOGIN_SHELL)) + argv[argc++] = g_strconcat ("-", only_name, NULL); + else + argv[argc++] = g_strdup (only_name); + + argv[argc++] = NULL; + + *spawn_flags_p |= G_SPAWN_FILE_AND_ARGV_ZERO; + } + + *argv_p = argv; + + return TRUE; +} + +static char** +get_child_environment (TerminalScreen *screen, + char **shell) +{ + TerminalScreenPrivate *priv = screen->priv; + GtkWidget *term = GTK_WIDGET (screen); + GtkWidget *window; + char **env; + char *e, *v; + GHashTable *env_table; + GHashTableIter iter; + GPtrArray *retval; + guint i; + + window = gtk_widget_get_toplevel (term); + g_assert (window != NULL); + g_assert (gtk_widget_is_toplevel (window)); + + env_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + /* First take the factory's environment */ + env = g_listenv (); + for (i = 0; env[i]; ++i) + g_hash_table_insert (env_table, env[i], g_strdup (g_getenv (env[i]))); + g_free (env); /* the strings themselves are now owned by the hash table */ + + /* and then merge the child environment, if any */ + env = priv->initial_env; + if (env) + { + for (i = 0; env[i]; ++i) + { + v = strchr (env[i], '='); + if (v) + g_hash_table_replace (env_table, g_strndup (env[i], v - env[i]), g_strdup (v + 1)); + else + g_hash_table_replace (env_table, g_strdup (env[i]), NULL); + } + } + + g_hash_table_remove (env_table, "COLUMNS"); + g_hash_table_remove (env_table, "LINES"); + g_hash_table_remove (env_table, "MATE_DESKTOP_ICON"); + + g_hash_table_replace (env_table, g_strdup ("COLORTERM"), g_strdup (EXECUTABLE_NAME)); + g_hash_table_replace (env_table, g_strdup ("TERM"), g_strdup ("xterm")); /* FIXME configurable later? */ + +#ifdef GDK_WINDOWING_X11 + /* FIXME: moving the tab between windows, or the window between displays will make the next two invalid... */ + g_hash_table_replace (env_table, g_strdup ("WINDOWID"), g_strdup_printf ("%ld", GDK_WINDOW_XWINDOW (gtk_widget_get_window (window)))); + g_hash_table_replace (env_table, g_strdup ("DISPLAY"), g_strdup (gdk_display_get_name (gtk_widget_get_display (window)))); +#endif + + terminal_util_add_proxy_env (env_table); + + retval = g_ptr_array_sized_new (g_hash_table_size (env_table)); + g_hash_table_iter_init (&iter, env_table); + while (g_hash_table_iter_next (&iter, (gpointer *) &e, (gpointer *) &v)) + g_ptr_array_add (retval, g_strdup_printf ("%s=%s", e, v ? v : "")); + g_ptr_array_add (retval, NULL); + + *shell = g_strdup (g_hash_table_lookup (env_table, "SHELL")); + + g_hash_table_destroy (env_table); + return (char **) g_ptr_array_free (retval, FALSE); +} + +enum { + RESPONSE_RELAUNCH, + RESPONSE_EDIT_PROFILE +}; + +static void +info_bar_response_cb (GtkWidget *info_bar, + int response, + TerminalScreen *screen) +{ + gtk_widget_grab_focus (GTK_WIDGET (screen)); + + switch (response) { + case GTK_RESPONSE_CANCEL: + gtk_widget_destroy (info_bar); + g_signal_emit (screen, signals[CLOSE_SCREEN], 0); + break; + case RESPONSE_RELAUNCH: + gtk_widget_destroy (info_bar); + terminal_screen_launch_child_on_idle (screen); + break; + case RESPONSE_EDIT_PROFILE: + terminal_app_edit_profile (terminal_app_get (), + terminal_screen_get_profile (screen), + GTK_WINDOW (terminal_screen_get_window (screen)), + "custom-command-entry"); + break; + default: + gtk_widget_destroy (info_bar); + break; + } +} + +static gboolean +terminal_screen_launch_child_cb (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + VteTerminal *terminal = VTE_TERMINAL (screen); + TerminalProfile *profile; + char **env, **argv; + char *shell = NULL; + GError *err = NULL; + const char *working_dir; + VtePtyFlags pty_flags = VTE_PTY_DEFAULT; + GSpawnFlags spawn_flags = 0; + GPid pid; + + priv->launch_child_source_id = 0; + + _terminal_debug_print (TERMINAL_DEBUG_PROCESSES, + "[screen %p] now launching the child process\n", + screen); + + profile = priv->profile; + + env = get_child_environment (screen, &shell); + + if (priv->initial_working_directory) + working_dir = priv->initial_working_directory; + else + working_dir = g_get_home_dir (); + + if (!terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_LOGIN_SHELL)) + pty_flags |= VTE_PTY_NO_LASTLOG; + if (!terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_UPDATE_RECORDS)) + pty_flags |= VTE_PTY_NO_UTMP | VTE_PTY_NO_WTMP; + + if (!get_child_command (screen, shell, &spawn_flags, &argv, &err) || + !vte_terminal_fork_command_full (terminal, + pty_flags, + working_dir, + argv, + env, + spawn_flags, + NULL, NULL, + &pid, + &err)) { + GtkWidget *info_bar; + + info_bar = terminal_info_bar_new (GTK_MESSAGE_ERROR, + _("_Profile Preferences"), RESPONSE_EDIT_PROFILE, + _("_Relaunch"), RESPONSE_RELAUNCH, + NULL); + terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar), + _("There was an error creating the child process for this terminal")); + terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar), + "%s", err->message); + g_signal_connect (info_bar, "response", + G_CALLBACK (info_bar_response_cb), screen); + + gtk_box_pack_start (GTK_BOX (terminal_screen_container_get_from_screen (screen)), + info_bar, FALSE, FALSE, 0); + gtk_info_bar_set_default_response (GTK_INFO_BAR (info_bar), GTK_RESPONSE_CANCEL); + gtk_widget_show (info_bar); + + g_error_free (err); + g_strfreev (env); + g_free (shell); + + return FALSE; + } + + priv->child_pid = pid; + priv->pty_fd = vte_terminal_get_pty (terminal); + + g_free (shell); + g_strfreev (argv); + g_strfreev (env); + + return FALSE; /* don't run again */ +} + +static void +terminal_screen_launch_child_on_idle (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + + if (priv->launch_child_source_id != 0) + return; + + _terminal_debug_print (TERMINAL_DEBUG_PROCESSES, + "[screen %p] scheduling launching the child process on idle\n", + screen); + + priv->launch_child_source_id = g_idle_add ((GSourceFunc) terminal_screen_launch_child_cb, screen); +} + +static TerminalScreenPopupInfo * +terminal_screen_popup_info_new (TerminalScreen *screen) +{ + TerminalScreenPopupInfo *info; + + info = g_slice_new0 (TerminalScreenPopupInfo); + info->ref_count = 1; + info->screen = g_object_ref (screen); + info->window = terminal_screen_get_window (screen); + + return info; +} + +TerminalScreenPopupInfo * +terminal_screen_popup_info_ref (TerminalScreenPopupInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + + info->ref_count++; + return info; +} + +void +terminal_screen_popup_info_unref (TerminalScreenPopupInfo *info) +{ + g_return_if_fail (info != NULL); + + if (--info->ref_count > 0) + return; + + g_object_unref (info->screen); + g_free (info->string); + g_slice_free (TerminalScreenPopupInfo, info); +} + +static gboolean +terminal_screen_popup_menu (GtkWidget *widget) +{ + TerminalScreen *screen = TERMINAL_SCREEN (widget); + TerminalScreenPopupInfo *info; + + info = terminal_screen_popup_info_new (screen); + info->button = 0; + info->timestamp = gtk_get_current_event_time (); + + g_signal_emit (screen, signals[SHOW_POPUP_MENU], 0, info); + terminal_screen_popup_info_unref (info); + + return TRUE; +} + +static gboolean +terminal_screen_button_press (GtkWidget *widget, + GdkEventButton *event) +{ + TerminalScreen *screen = TERMINAL_SCREEN (widget); + gboolean (* button_press_event) (GtkWidget*, GdkEventButton*) = + GTK_WIDGET_CLASS (terminal_screen_parent_class)->button_press_event; + int char_width, char_height, row, col; + char *matched_string; + int matched_flavor = 0; + guint state; + GtkBorder *inner_border = NULL; + + state = event->state & gtk_accelerator_get_default_mod_mask (); + + terminal_screen_get_cell_size (screen, &char_width, &char_height); + + gtk_widget_style_get (widget, "inner-border", &inner_border, NULL); + row = (event->x - (inner_border ? inner_border->left : 0)) / char_width; + col = (event->y - (inner_border ? inner_border->top : 0)) / char_height; + gtk_border_free (inner_border); + + /* FIXMEchpe: add vte API to do this check by widget coords instead of grid coords */ + matched_string = terminal_screen_check_match (screen, row, col, &matched_flavor); + + if (matched_string != NULL && + (event->button == 1 || event->button == 2) && + (state & GDK_CONTROL_MASK)) + { + gboolean handled = FALSE; + +#ifdef ENABLE_SKEY + if (matched_flavor != FLAVOR_SKEY || + terminal_profile_get_property_boolean (screen->priv->profile, TERMINAL_PROFILE_USE_SKEY)) +#endif + { + g_signal_emit (screen, signals[MATCH_CLICKED], 0, + matched_string, + matched_flavor, + state, + &handled); + } + + g_free (matched_string); + + if (handled) + return TRUE; /* don't do anything else such as select with the click */ + } + + if (event->button == 3 && + (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)) == 0) + { + TerminalScreenPopupInfo *info; + + info = terminal_screen_popup_info_new (screen); + info->button = event->button; + info->state = state; + info->timestamp = event->time; + info->string = matched_string; /* adopted */ + info->flavour = matched_flavor; + + g_signal_emit (screen, signals[SHOW_POPUP_MENU], 0, info); + terminal_screen_popup_info_unref (info); + + return TRUE; + } + + /* default behavior is to let the terminal widget deal with it */ + if (button_press_event) + return button_press_event (widget, event); + + return FALSE; +} + +static void +terminal_screen_set_dynamic_title (TerminalScreen *screen, + const char *title, + gboolean userset) +{ + TerminalScreenPrivate *priv = screen->priv; + + g_assert (TERMINAL_IS_SCREEN (screen)); + + if ((priv->user_title && !userset) || + (priv->raw_title && title && + strcmp (priv->raw_title, title) == 0)) + return; + + g_free (priv->raw_title); + priv->raw_title = g_strdup (title); + terminal_screen_cook_title (screen); +} + +static void +terminal_screen_set_dynamic_icon_title (TerminalScreen *screen, + const char *icon_title, + gboolean userset) +{ + TerminalScreenPrivate *priv = screen->priv; + GObject *object = G_OBJECT (screen); + + g_assert (TERMINAL_IS_SCREEN (screen)); + + if ((priv->user_title && !userset) || + (priv->icon_title_set && + priv->raw_icon_title && + icon_title && + strcmp (priv->raw_icon_title, icon_title) == 0)) + return; + + g_object_freeze_notify (object); + + g_free (priv->raw_icon_title); + priv->raw_icon_title = g_strdup (icon_title); + priv->icon_title_set = TRUE; + + g_object_notify (object, "icon-title-set"); + terminal_screen_cook_icon_title (screen); + + g_object_thaw_notify (object); +} + +void +terminal_screen_set_override_title (TerminalScreen *screen, + const char *title) +{ + TerminalScreenPrivate *priv = screen->priv; + char *old_title; + + old_title = priv->override_title; + priv->override_title = g_strdup (title); + g_free (old_title); + + terminal_screen_set_dynamic_title (screen, title, FALSE); + terminal_screen_set_dynamic_icon_title (screen, title, FALSE); +} + +const char* +terminal_screen_get_dynamic_title (TerminalScreen *screen) +{ + g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), NULL); + + return screen->priv->raw_title; +} + +const char* +terminal_screen_get_dynamic_icon_title (TerminalScreen *screen) +{ + g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), NULL); + + return screen->priv->raw_icon_title; +} + +/** + * terminal_screen_get_current_dir: + * @screen: + * + * Tries to determine the current working directory of the foreground process + * in @screen's PTY, falling back to the current working directory of the + * primary child. + * + * Returns: a newly allocated string containing the current working directory, + * or %NULL on failure + */ +char* +terminal_screen_get_current_dir (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + char *cwd; + + if (priv->pty_fd != -1) { +#if 0 + /* Get the foreground process ID */ + cwd = cwd_of_pid (tcgetpgrp (priv->pty_fd)); + if (cwd != NULL) + return cwd; +#endif + + /* If that didn't work, try falling back to the primary child. See bug #575184. */ + cwd = cwd_of_pid (priv->child_pid); + if (cwd != NULL) + return cwd; + } + + return NULL; +} + +/** + * terminal_screen_get_current_dir_with_fallback: + * @screen: + * + * Like terminal_screen_get_current_dir(), but falls back to returning + * @screen's initial working directory, with a further fallback to the + * user's home directory. + * + * Returns: a newly allocated string containing the current working directory, + * or %NULL on failure + */ +char* +terminal_screen_get_current_dir_with_fallback (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + + if (priv->pty_fd == -1) + return g_strdup (priv->initial_working_directory); + + return terminal_screen_get_current_dir (screen); +} + +void +terminal_screen_set_font_scale (TerminalScreen *screen, + double factor) +{ + TerminalScreenPrivate *priv = screen->priv; + + g_return_if_fail (TERMINAL_IS_SCREEN (screen)); + + if (factor < TERMINAL_SCALE_MINIMUM) + factor = TERMINAL_SCALE_MINIMUM; + if (factor > TERMINAL_SCALE_MAXIMUM) + factor = TERMINAL_SCALE_MAXIMUM; + + priv->font_scale = factor; + + if (gtk_widget_get_realized (GTK_WIDGET (screen))) + { + /* Update the font */ + terminal_screen_change_font (screen); + } +} + +double +terminal_screen_get_font_scale (TerminalScreen *screen) +{ + g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), 1.0); + + return screen->priv->font_scale; +} + +static void +terminal_screen_window_title_changed (VteTerminal *vte_terminal, + TerminalScreen *screen) +{ + terminal_screen_set_dynamic_title (screen, + vte_terminal_get_window_title (vte_terminal), + FALSE); +} + +static void +terminal_screen_icon_title_changed (VteTerminal *vte_terminal, + TerminalScreen *screen) +{ + terminal_screen_set_dynamic_icon_title (screen, + vte_terminal_get_icon_title (vte_terminal), + FALSE); +} + +static void +terminal_screen_child_exited (VteTerminal *terminal) +{ + TerminalScreen *screen = TERMINAL_SCREEN (terminal); + TerminalScreenPrivate *priv = screen->priv; + TerminalExitAction action; + + /* No need to chain up to VteTerminalClass::child_exited since it's NULL */ + + _terminal_debug_print (TERMINAL_DEBUG_PROCESSES, + "[screen %p] child process exited\n", + screen); + + priv->child_pid = -1; + priv->pty_fd = -1; + + action = terminal_profile_get_property_enum (priv->profile, TERMINAL_PROFILE_EXIT_ACTION); + + switch (action) + { + case TERMINAL_EXIT_CLOSE: + g_signal_emit (screen, signals[CLOSE_SCREEN], 0); + break; + case TERMINAL_EXIT_RESTART: + terminal_screen_launch_child_on_idle (screen); + break; + case TERMINAL_EXIT_HOLD: { + GtkWidget *info_bar; + int status; + + status = vte_terminal_get_child_exit_status (terminal); + + info_bar = terminal_info_bar_new (GTK_MESSAGE_INFO, + _("_Relaunch"), RESPONSE_RELAUNCH, + NULL); + if (WIFEXITED (status)) { + terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar), + _("The child process exited normally with status %d."), WEXITSTATUS (status)); + } else if (WIFSIGNALED (status)) { + terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar), + _("The child process was terminated by signal %d."), WTERMSIG (status)); + } else { + terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar), + _("The child process was terminated.")); + } + g_signal_connect (info_bar, "response", + G_CALLBACK (info_bar_response_cb), screen); + + gtk_box_pack_start (GTK_BOX (terminal_screen_container_get_from_screen (screen)), + info_bar, FALSE, FALSE, 0); + gtk_info_bar_set_default_response (GTK_INFO_BAR (info_bar), RESPONSE_RELAUNCH); + gtk_widget_show (info_bar); + break; + } + + default: + break; + } +} + +void +terminal_screen_set_user_title (TerminalScreen *screen, + const char *text) +{ + TerminalScreenPrivate *priv = screen->priv; + + /* The user set the title to nothing, let's understand that as a + request to revert to dynamically setting the title again. */ + if (!text || !text[0]) + priv->user_title = FALSE; + else + { + priv->user_title = TRUE; + terminal_screen_set_dynamic_title (screen, text, TRUE); + terminal_screen_set_dynamic_icon_title (screen, text, TRUE); + } +} + +static void +terminal_screen_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint timestamp) +{ + TerminalScreen *screen = TERMINAL_SCREEN (widget); + TerminalScreenPrivate *priv = screen->priv; + const guchar *selection_data_data; + GdkAtom selection_data_target; + gint selection_data_length, selection_data_format; + + selection_data_data = gtk_selection_data_get_data (selection_data); + selection_data_target = gtk_selection_data_get_target (selection_data); + selection_data_length = gtk_selection_data_get_length (selection_data); + selection_data_format = gtk_selection_data_get_format (selection_data); + +#if 0 + { + GList *tmp; + + g_print ("info: %d\n", info); + tmp = context->targets; + while (tmp != NULL) + { + GdkAtom atom = GDK_POINTER_TO_ATOM (tmp->data); + + g_print ("Target: %s\n", gdk_atom_name (atom)); + + tmp = tmp->next; + } + + g_print ("Chosen target: %s\n", gdk_atom_name (selection_data->target)); + } +#endif + + if (gtk_targets_include_uri (&selection_data_target, 1)) + { + char **uris; + char *text; + gsize len; + + uris = gtk_selection_data_get_uris (selection_data); + if (!uris) + return; + + terminal_util_transform_uris_to_quoted_fuse_paths (uris); + + text = terminal_util_concat_uris (uris, &len); + vte_terminal_feed_child (VTE_TERMINAL (screen), text, len); + g_free (text); + + g_strfreev (uris); + } + else if (gtk_targets_include_text (&selection_data_target, 1)) + { + char *text; + + text = (char *) gtk_selection_data_get_text (selection_data); + if (text && text[0]) + vte_terminal_feed_child (VTE_TERMINAL (screen), text, strlen (text)); + g_free (text); + } + else switch (info) + { + case TARGET_COLOR: + { + guint16 *data = (guint16 *)selection_data_data; + GdkColor color; + + /* We accept drops with the wrong format, since the KDE color + * chooser incorrectly drops application/x-color with format 8. + * So just check for the data length. + */ + if (selection_data_length != 8) + return; + + color.red = data[0]; + color.green = data[1]; + color.blue = data[2]; + /* FIXME: use opacity from data[3] */ + + g_object_set (priv->profile, + TERMINAL_PROFILE_BACKGROUND_TYPE, TERMINAL_BACKGROUND_SOLID, + TERMINAL_PROFILE_USE_THEME_COLORS, FALSE, + TERMINAL_PROFILE_BACKGROUND_COLOR, &color, + NULL); + } + break; + + case TARGET_MOZ_URL: + { + char *utf8_data, *newline, *text; + char *uris[2]; + gsize len; + + /* MOZ_URL is in UCS-2 but in format 8. BROKEN! + * + * The data contains the URL, a \n, then the + * title of the web page. + */ + if (selection_data_format != 8 || + selection_data_length == 0 || + (selection_data_length % 2) != 0) + return; + + utf8_data = g_utf16_to_utf8 ((const gunichar2*) selection_data_data, + selection_data_length / 2, + NULL, NULL, NULL); + if (!utf8_data) + return; + + newline = strchr (utf8_data, '\n'); + if (newline) + *newline = '\0'; + + uris[0] = utf8_data; + uris[1] = NULL; + terminal_util_transform_uris_to_quoted_fuse_paths (uris); /* This may replace uris[0] */ + + text = terminal_util_concat_uris (uris, &len); + vte_terminal_feed_child (VTE_TERMINAL (screen), text, len); + g_free (text); + g_free (uris[0]); + } + break; + + case TARGET_NETSCAPE_URL: + { + char *utf8_data, *newline, *text; + char *uris[2]; + gsize len; + + /* The data contains the URL, a \n, then the + * title of the web page. + */ + if (selection_data_length < 0 || selection_data_format != 8) + return; + + utf8_data = g_strndup ((char *) selection_data_data, selection_data_length); + newline = strchr (utf8_data, '\n'); + if (newline) + *newline = '\0'; + + uris[0] = utf8_data; + uris[1] = NULL; + terminal_util_transform_uris_to_quoted_fuse_paths (uris); /* This may replace uris[0] */ + + text = terminal_util_concat_uris (uris, &len); + vte_terminal_feed_child (VTE_TERMINAL (screen), text, len); + g_free (text); + g_free (uris[0]); + } + break; + + case TARGET_BGIMAGE: + { + char *utf8_data; + char **uris; + + if (selection_data_length < 0 || selection_data_format != 8) + return; + + utf8_data = g_strndup ((char *) selection_data_data, selection_data_length); + uris = g_uri_list_extract_uris (utf8_data); + g_free (utf8_data); + + /* FIXME: use terminal_util_transform_uris_to_quoted_fuse_paths? */ + + if (uris && uris[0]) + { + char *filename; + + filename = g_filename_from_uri (uris[0], NULL, NULL); + if (filename) + { + g_object_set (priv->profile, + TERMINAL_PROFILE_BACKGROUND_TYPE, TERMINAL_BACKGROUND_IMAGE, + TERMINAL_PROFILE_BACKGROUND_IMAGE_FILE, filename, + NULL); + } + + g_free (filename); + } + + g_strfreev (uris); + } + break; + + case TARGET_RESET_BG: + g_object_set (priv->profile, + TERMINAL_PROFILE_BACKGROUND_TYPE, TERMINAL_BACKGROUND_SOLID, + NULL); + break; + + case TARGET_TAB: + { + GtkWidget *container; + TerminalScreen *moving_screen; + TerminalWindow *source_window; + TerminalWindow *dest_window; + GtkWidget *dest_notebook; + int page_num; + + container = *(GtkWidget**) selection_data_data; + if (!GTK_IS_WIDGET (container)) + return; + + moving_screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (container)); + g_return_if_fail (TERMINAL_IS_SCREEN (moving_screen)); + if (!TERMINAL_IS_SCREEN (moving_screen)) + return; + + source_window = terminal_screen_get_window (moving_screen); + dest_window = terminal_screen_get_window (screen); + dest_notebook = terminal_window_get_notebook (dest_window); + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (dest_notebook), + GTK_WIDGET (screen)); + terminal_window_move_screen (source_window, dest_window, moving_screen, page_num + 1); + + gtk_drag_finish (context, TRUE, TRUE, timestamp); + } + break; + + default: + g_assert_not_reached (); + } +} + +void +_terminal_screen_update_scrollbar (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + TerminalScreenContainer *container; + GtkPolicyType policy = GTK_POLICY_ALWAYS; + GtkCornerType corner = GTK_CORNER_TOP_LEFT; + + container = terminal_screen_container_get_from_screen (screen); + if (container == NULL) + return; + + switch (terminal_profile_get_property_enum (priv->profile, TERMINAL_PROFILE_SCROLLBAR_POSITION)) + { + case TERMINAL_SCROLLBAR_HIDDEN: + policy = GTK_POLICY_NEVER; + break; + case TERMINAL_SCROLLBAR_RIGHT: + policy = GTK_POLICY_ALWAYS; + corner = GTK_CORNER_TOP_LEFT; + break; + case TERMINAL_SCROLLBAR_LEFT: + policy = GTK_POLICY_ALWAYS; + corner = GTK_CORNER_TOP_RIGHT; + break; + default: + g_assert_not_reached (); + break; + } + + terminal_screen_container_set_placement (container, corner); + terminal_screen_container_set_policy (container, GTK_POLICY_NEVER, policy); +} + +void +terminal_screen_get_size (TerminalScreen *screen, + int *width_chars, + int *height_chars) +{ + VteTerminal *terminal = VTE_TERMINAL (screen); + + *width_chars = vte_terminal_get_column_count (terminal); + *height_chars = vte_terminal_get_row_count (terminal); +} + +void +terminal_screen_get_cell_size (TerminalScreen *screen, + int *cell_width_pixels, + int *cell_height_pixels) +{ + VteTerminal *terminal = VTE_TERMINAL (screen); + + *cell_width_pixels = vte_terminal_get_char_width (terminal); + *cell_height_pixels = vte_terminal_get_char_height (terminal); +} + +#ifdef ENABLE_SKEY +static void +terminal_screen_skey_match_remove (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + GSList *l, *next; + + l = priv->match_tags; + while (l != NULL) + { + TagData *tag_data = (TagData *) l->data; + + next = l->next; + if (tag_data->flavor == FLAVOR_SKEY) + { + vte_terminal_match_remove (VTE_TERMINAL (screen), tag_data->tag); + priv->match_tags = g_slist_delete_link (priv->match_tags, l); + } + + l = next; + } +} +#endif /* ENABLE_SKEY */ + +static char* +terminal_screen_check_match (TerminalScreen *screen, + int column, + int row, + int *flavor) +{ + TerminalScreenPrivate *priv = screen->priv; + GSList *tags; + int tag; + char *match; + + match = vte_terminal_match_check (VTE_TERMINAL (screen), column, row, &tag); + for (tags = priv->match_tags; tags != NULL; tags = tags->next) + { + TagData *tag_data = (TagData*) tags->data; + if (tag_data->tag == tag) + { + if (flavor) + *flavor = tag_data->flavor; + return match; + } + } + + g_free (match); + return NULL; +} + +void +terminal_screen_save_config (TerminalScreen *screen, + GKeyFile *key_file, + const char *group) +{ + TerminalScreenPrivate *priv = screen->priv; + VteTerminal *terminal = VTE_TERMINAL (screen); + TerminalProfile *profile = priv->profile; + const char *profile_id; + char *working_directory; + + profile_id = terminal_profile_get_property_string (profile, TERMINAL_PROFILE_NAME); + g_key_file_set_string (key_file, group, TERMINAL_CONFIG_TERMINAL_PROP_PROFILE_ID, profile_id); + + if (priv->override_command) + terminal_util_key_file_set_argv (key_file, group, TERMINAL_CONFIG_TERMINAL_PROP_COMMAND, + -1, priv->override_command); + + if (priv->override_title) + g_key_file_set_string (key_file, group, TERMINAL_CONFIG_TERMINAL_PROP_TITLE, priv->override_title); + + /* FIXMEchpe: use the initial_working_directory instead?? */ + working_directory = terminal_screen_get_current_dir (screen); + if (working_directory) + terminal_util_key_file_set_string_escape (key_file, group, TERMINAL_CONFIG_TERMINAL_PROP_WORKING_DIRECTORY, working_directory); + g_free (working_directory); + + g_key_file_set_double (key_file, group, TERMINAL_CONFIG_TERMINAL_PROP_ZOOM, priv->font_scale); + + g_key_file_set_integer (key_file, group, TERMINAL_CONFIG_TERMINAL_PROP_WIDTH, + vte_terminal_get_column_count (terminal)); + g_key_file_set_integer (key_file, group, TERMINAL_CONFIG_TERMINAL_PROP_HEIGHT, + vte_terminal_get_row_count (terminal)); +} + +/** + * terminal_screen_has_foreground_process: + * @screen: + * + * Checks whether there's a foreground process running in + * this terminal. + * + * Returns: %TRUE iff there's a foreground process running in @screen + */ +gboolean +terminal_screen_has_foreground_process (TerminalScreen *screen) +{ + TerminalScreenPrivate *priv = screen->priv; + int fgpid; + + if (priv->pty_fd == -1) + return FALSE; + + fgpid = tcgetpgrp (priv->pty_fd); + if (fgpid == -1 || fgpid == priv->child_pid) + return FALSE; + + return TRUE; + +#if 0 + char *cmdline, *basename, *name; + gsize len; + char filename[64]; + + g_snprintf (filename, sizeof (filename), "/proc/%d/cmdline", fgpid); + if (!g_file_get_contents (filename, &cmdline, &len, NULL)) + return TRUE; + + basename = g_path_get_basename (cmdline); + g_free (cmdline); + if (!basename) + return TRUE; + + name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL); + g_free (basename); + if (!name) + return TRUE; + + if (process_name) + *process_name = name; + + return TRUE; +#endif +} diff --git a/src/terminal-screen.h b/src/terminal-screen.h new file mode 100644 index 0000000..885e7d2 --- /dev/null +++ b/src/terminal-screen.h @@ -0,0 +1,160 @@ +/* + * Copyright © 2001 Havoc Pennington + * Copyright © 2008 Christian Persch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TERMINAL_SCREEN_H +#define TERMINAL_SCREEN_H + +#include <vte/vte.h> + +#include "terminal-profile.h" + +G_BEGIN_DECLS + +typedef enum { + FLAVOR_AS_IS, + FLAVOR_DEFAULT_TO_HTTP, + FLAVOR_VOIP_CALL, + FLAVOR_EMAIL, + FLAVOR_SKEY +} TerminalURLFlavour; + +/* Forward decls */ +typedef struct _TerminalScreenPopupInfo TerminalScreenPopupInfo; +typedef struct _TerminalWindow TerminalWindow; + +#define TERMINAL_TYPE_SCREEN (terminal_screen_get_type ()) +#define TERMINAL_SCREEN(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TERMINAL_TYPE_SCREEN, TerminalScreen)) +#define TERMINAL_SCREEN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TERMINAL_TYPE_SCREEN, TerminalScreenClass)) +#define TERMINAL_IS_SCREEN(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TERMINAL_TYPE_SCREEN)) +#define TERMINAL_IS_SCREEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TERMINAL_TYPE_SCREEN)) +#define TERMINAL_SCREEN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TERMINAL_TYPE_SCREEN, TerminalScreenClass)) + +typedef struct _TerminalScreen TerminalScreen; +typedef struct _TerminalScreenClass TerminalScreenClass; +typedef struct _TerminalScreenPrivate TerminalScreenPrivate; + +struct _TerminalScreen +{ + VteTerminal parent_instance; + + TerminalScreenPrivate *priv; +}; + +struct _TerminalScreenClass +{ + VteTerminalClass parent_class; + + void (* profile_set) (TerminalScreen *screen, + TerminalProfile *old_profile); + void (* show_popup_menu) (TerminalScreen *screen, + TerminalScreenPopupInfo *info); + gboolean (* match_clicked) (TerminalScreen *screen, + const char *url, + int flavour, + guint state); + void (* close_screen) (TerminalScreen *screen); +}; + +GType terminal_screen_get_type (void) G_GNUC_CONST; + +TerminalScreen *terminal_screen_new (TerminalProfile *profile, + char **override_command, + const char *title, + const char *working_dir, + char **child_env, + double zoom); + +void terminal_screen_set_profile (TerminalScreen *screen, + TerminalProfile *profile); +TerminalProfile* terminal_screen_get_profile (TerminalScreen *screen); + +void terminal_screen_set_override_command (TerminalScreen *screen, + char **argv); +const char** terminal_screen_get_override_command (TerminalScreen *screen); + +void terminal_screen_set_initial_environment (TerminalScreen *screen, + char **argv); +char ** terminal_screen_get_initial_environment (TerminalScreen *screen); + +const char* terminal_screen_get_raw_title (TerminalScreen *screen); +const char* terminal_screen_get_title (TerminalScreen *screen); +const char* terminal_screen_get_icon_title (TerminalScreen *screen); +gboolean terminal_screen_get_icon_title_set (TerminalScreen *screen); + +void terminal_screen_set_user_title (TerminalScreen *screen, + const char *text); + +void terminal_screen_set_override_title (TerminalScreen *screen, + const char *title); + +const char *terminal_screen_get_dynamic_title (TerminalScreen *screen); +const char *terminal_screen_get_dynamic_icon_title (TerminalScreen *screen); + +char *terminal_screen_get_current_dir (TerminalScreen *screen); +char *terminal_screen_get_current_dir_with_fallback (TerminalScreen *screen); + +void terminal_screen_set_font (TerminalScreen *screen); +void terminal_screen_set_font_scale (TerminalScreen *screen, + double factor); +double terminal_screen_get_font_scale (TerminalScreen *screen); + +void terminal_screen_get_size (TerminalScreen *screen, + int *width_chars, + int *height_chars); +void terminal_screen_get_cell_size (TerminalScreen *screen, + int *width_chars, + int *height_chars); + +void _terminal_screen_update_scrollbar (TerminalScreen *screen); + +void terminal_screen_save_config (TerminalScreen *screen, + GKeyFile *key_file, + const char *group); + +gboolean terminal_screen_has_foreground_process (TerminalScreen *screen); + +/* Allow scales a bit smaller and a bit larger than the usual pango ranges */ +#define TERMINAL_SCALE_XXX_SMALL (PANGO_SCALE_XX_SMALL/1.2) +#define TERMINAL_SCALE_XXXX_SMALL (TERMINAL_SCALE_XXX_SMALL/1.2) +#define TERMINAL_SCALE_XXXXX_SMALL (TERMINAL_SCALE_XXXX_SMALL/1.2) +#define TERMINAL_SCALE_XXX_LARGE (PANGO_SCALE_XX_LARGE*1.2) +#define TERMINAL_SCALE_XXXX_LARGE (TERMINAL_SCALE_XXX_LARGE*1.2) +#define TERMINAL_SCALE_XXXXX_LARGE (TERMINAL_SCALE_XXXX_LARGE*1.2) +#define TERMINAL_SCALE_MINIMUM (TERMINAL_SCALE_XXXXX_SMALL/1.2) +#define TERMINAL_SCALE_MAXIMUM (TERMINAL_SCALE_XXXXX_LARGE*1.2) + +struct _TerminalScreenPopupInfo { + int ref_count; + TerminalWindow *window; + TerminalScreen *screen; + char *string; + TerminalURLFlavour flavour; + guint button; + guint state; + guint32 timestamp; +}; + +TerminalScreenPopupInfo *terminal_screen_popup_info_ref (TerminalScreenPopupInfo *info); + +void terminal_screen_popup_info_unref (TerminalScreenPopupInfo *info); + +G_END_DECLS + +#endif /* TERMINAL_SCREEN_H */ diff --git a/src/terminal-search-dialog.c b/src/terminal-search-dialog.c new file mode 100644 index 0000000..81e3317 --- /dev/null +++ b/src/terminal-search-dialog.c @@ -0,0 +1,378 @@ +/* + * Copyright © 2005 Paolo Maggi + * Copyright © 2010 Red Hat (Red Hat author: Behdad Esfahbod) + * + * 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 3 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 <string.h> + +#include "terminal-search-dialog.h" +#include "terminal-util.h" + +#define HISTORY_MIN_ITEM_LEN 3 +#define HISTORY_LENGTH 10 + +static GQuark +get_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("GT:data"); + + return quark; +} + + +#define TERMINAL_SEARCH_DIALOG_GET_PRIVATE(object) \ + ((TerminalSearchDialogPrivate *) g_object_get_qdata (G_OBJECT (object), get_quark ())) + +#define GET_FLAG(widget) gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->widget)) + +typedef struct _TerminalSearchDialogPrivate +{ + GtkWidget *search_label; + GtkWidget *search_entry; + GtkWidget *search_text_entry; + GtkWidget *match_case_checkbutton; + GtkWidget *entire_word_checkbutton; + GtkWidget *regex_checkbutton; + GtkWidget *backwards_checkbutton; + GtkWidget *wrap_around_checkbutton; + + GtkListStore *store; + GtkEntryCompletion *completion; + + /* Cached regex */ + GRegex *regex; + GRegexCompileFlags regex_compile_flags; +} TerminalSearchDialogPrivate; + + +static void update_sensitivity (void *unused, + GtkWidget *dialog); +static void response_handler (GtkWidget *dialog, + gint response_id, + gpointer data); +static void terminal_search_dialog_private_destroy (TerminalSearchDialogPrivate *priv); + + +GtkWidget * +terminal_search_dialog_new (GtkWindow *parent) +{ + GtkWidget *dialog; + TerminalSearchDialogPrivate *priv; + GtkListStore *store; + GtkEntryCompletion *completion; + + priv = g_new0 (TerminalSearchDialogPrivate, 1); + + if (!terminal_util_load_builder_file ("find-dialog.ui", + "find-dialog", &dialog, + "search-label", &priv->search_label, + "search-entry", &priv->search_entry, + "match-case-checkbutton", &priv->match_case_checkbutton, + "entire-word-checkbutton", &priv->entire_word_checkbutton, + "regex-checkbutton", &priv->regex_checkbutton, + "search-backwards-checkbutton", &priv->backwards_checkbutton, + "wrap-around-checkbutton", &priv->wrap_around_checkbutton, + NULL)) + { + g_free (priv); + return NULL; + } + + g_object_set_qdata_full (G_OBJECT (dialog), get_quark (), priv, + (GDestroyNotify) terminal_search_dialog_private_destroy); + + + priv->search_text_entry = gtk_bin_get_child (GTK_BIN (priv->search_entry)); + gtk_widget_set_size_request (priv->search_entry, 300, -1); + + priv->store = store = gtk_list_store_new (1, G_TYPE_STRING); + g_object_set (G_OBJECT (priv->search_entry), + "model", store, + "text-column", 0, + NULL); + + priv->completion = completion = gtk_entry_completion_new (); + gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (store)); + gtk_entry_completion_set_text_column (completion, 0); + gtk_entry_completion_set_minimum_key_length (completion, HISTORY_MIN_ITEM_LEN); + gtk_entry_completion_set_popup_completion (completion, FALSE); + gtk_entry_completion_set_inline_completion (completion, TRUE); + gtk_entry_set_completion (GTK_ENTRY (priv->search_text_entry), completion); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, FALSE); + + gtk_entry_set_activates_default (GTK_ENTRY (priv->search_text_entry), TRUE); + g_signal_connect (priv->search_text_entry, "changed", G_CALLBACK (update_sensitivity), dialog); + g_signal_connect (priv->regex_checkbutton, "toggled", G_CALLBACK (update_sensitivity), dialog); + + g_signal_connect (dialog, "response", G_CALLBACK (response_handler), NULL); + + if (parent) + gtk_window_set_transient_for (GTK_WINDOW (dialog), parent); + + return GTK_WIDGET (dialog); +} + +void +terminal_search_dialog_present (GtkWidget *dialog) +{ + TerminalSearchDialogPrivate *priv; + + g_return_if_fail (GTK_IS_DIALOG (dialog)); + + priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog); + g_return_if_fail (priv); + + gtk_window_present (GTK_WINDOW (dialog)); + gtk_widget_grab_focus (priv->search_text_entry); +} + +static void +terminal_search_dialog_private_destroy (TerminalSearchDialogPrivate *priv) +{ + + if (priv->regex) + g_regex_unref (priv->regex); + + g_object_unref (priv->store); + g_object_unref (priv->completion); + + g_free (priv); +} + + +static void +update_sensitivity (void *unused, GtkWidget *dialog) +{ + TerminalSearchDialogPrivate *priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog); + const gchar *search_string; + gboolean valid; + + if (priv->regex) { + g_regex_unref (priv->regex); + priv->regex = NULL; + } + + search_string = gtk_entry_get_text (GTK_ENTRY (priv->search_text_entry)); + g_return_if_fail (search_string != NULL); + + valid = *search_string != '\0'; + + if (valid && GET_FLAG (regex_checkbutton)) { + /* Check that the regex is valid */ + valid = NULL != terminal_search_dialog_get_regex (dialog); + /* TODO show the error message somewhere */ + } + + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, valid); +} + +static gboolean +remove_item (GtkListStore *store, + const gchar *text) +{ + GtkTreeIter iter; + + g_return_val_if_fail (text != NULL, FALSE); + + if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) + return FALSE; + + do { + gchar *item_text; + + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &item_text, -1); + + if (item_text != NULL && strcmp (item_text, text) == 0) { + gtk_list_store_remove (store, &iter); + g_free (item_text); + return TRUE; + } + + g_free (item_text); + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); + + return FALSE; +} + +static void +clamp_list_store (GtkListStore *store, + guint max) +{ + GtkTreePath *path; + GtkTreeIter iter; + + /* -1 because TreePath counts from 0 */ + path = gtk_tree_path_new_from_indices (max - 1, -1); + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path)) + while (1) + if (!gtk_list_store_remove (store, &iter)) + break; + + gtk_tree_path_free (path); +} + +static void +history_entry_insert (GtkListStore *store, + const gchar *text) +{ + GtkTreeIter iter; + + g_return_if_fail (text != NULL); + + if (g_utf8_strlen (text, -1) <= HISTORY_MIN_ITEM_LEN) + return; + + /* remove the text from the store if it was already + * present. If it wasn't, clamp to max history - 1 + * before inserting the new row, otherwise appending + * would not work */ + + if (!remove_item (store, text)) + clamp_list_store (store, HISTORY_LENGTH - 1); + + gtk_list_store_insert (store, &iter, 0); + gtk_list_store_set (store, &iter, 0, text, -1); +} + +static void +response_handler (GtkWidget *dialog, + gint response_id, + gpointer data) +{ + TerminalSearchDialogPrivate *priv; + const gchar *str; + + if (response_id != GTK_RESPONSE_ACCEPT) { + gtk_widget_hide (dialog); + return; + } + + priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog); + + str = gtk_entry_get_text (GTK_ENTRY (priv->search_text_entry)); + if (*str != '\0') + history_entry_insert (priv->store, str); +} + + +void +terminal_search_dialog_set_search_text (GtkWidget *dialog, + const gchar *text) +{ + TerminalSearchDialogPrivate *priv; + + g_return_if_fail (GTK_IS_DIALOG (dialog)); + g_return_if_fail (text != NULL); + + priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog); + g_return_if_fail (priv); + + gtk_entry_set_text (GTK_ENTRY (priv->search_text_entry), text); + + gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), + GTK_RESPONSE_ACCEPT, + (*text != '\0')); +} + +const gchar * +terminal_search_dialog_get_search_text (GtkWidget *dialog) +{ + TerminalSearchDialogPrivate *priv; + + g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL); + + priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog); + g_return_val_if_fail (priv, NULL); + + return gtk_entry_get_text (GTK_ENTRY (priv->search_text_entry)); +} + +TerminalSearchFlags +terminal_search_dialog_get_search_flags (GtkWidget *dialog) +{ + TerminalSearchDialogPrivate *priv; + TerminalSearchFlags flags = 0; + + g_return_val_if_fail (GTK_IS_DIALOG (dialog), flags); + + priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog); + g_return_val_if_fail (priv, flags); + + if (GET_FLAG (backwards_checkbutton)) + flags |= TERMINAL_SEARCH_FLAG_BACKWARDS; + + if (GET_FLAG (wrap_around_checkbutton)) + flags |= TERMINAL_SEARCH_FLAG_WRAP_AROUND; + + return flags; +} + +GRegex * +terminal_search_dialog_get_regex (GtkWidget *dialog) +{ + TerminalSearchDialogPrivate *priv; + GRegexCompileFlags compile_flags; + const char *text, *pattern; + + g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL); + + priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog); + g_return_val_if_fail (priv, NULL); + + pattern = text = terminal_search_dialog_get_search_text (dialog); + + compile_flags = G_REGEX_OPTIMIZE; + + if (!GET_FLAG (match_case_checkbutton)) + compile_flags |= G_REGEX_CASELESS; + + if (GET_FLAG (regex_checkbutton)) + compile_flags |= G_REGEX_MULTILINE; + else + pattern = g_regex_escape_string (text, -1); + + if (GET_FLAG (entire_word_checkbutton)) { + const char *old_pattern = pattern; + pattern = g_strdup_printf ("\\b%s\\b", pattern); + if (old_pattern != text) + g_free ((char *) old_pattern); + } + + if (!priv->regex || priv->regex_compile_flags != compile_flags) { + priv->regex_compile_flags = compile_flags; + if (priv->regex) + g_regex_unref (priv->regex); + + /* TODO Error handling */ + priv->regex = g_regex_new (pattern, compile_flags, 0, NULL); + } + + if (pattern != text) + g_free ((char *) pattern); + + return priv->regex; +} + diff --git a/src/terminal-search-dialog.h b/src/terminal-search-dialog.h new file mode 100644 index 0000000..e8911da --- /dev/null +++ b/src/terminal-search-dialog.h @@ -0,0 +1,49 @@ +/* + * Copyright © 2005 Paolo Maggi + * Copyright © 2010 Red Hat (Red Hat author: Behdad Esfahbod) + * + * 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 3 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 TERMINAL_SEARCH_DIALOG_H +#define TERMINAL_SEARCH_DIALOG_H + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +typedef enum _TerminalSearchFlags { + TERMINAL_SEARCH_FLAG_BACKWARDS = 1 << 0, + TERMINAL_SEARCH_FLAG_WRAP_AROUND = 1 << 1 +} TerminalSearchFlags; + + +GtkWidget *terminal_search_dialog_new (GtkWindow *parent); + +void terminal_search_dialog_present (GtkWidget *dialog); + +void terminal_search_dialog_set_search_text (GtkWidget *dialog, + const gchar *text); + +const gchar *terminal_search_dialog_get_search_text (GtkWidget *dialog); + +TerminalSearchFlags + terminal_search_dialog_get_search_flags(GtkWidget *dialog); +GRegex *terminal_search_dialog_get_regex (GtkWidget *dialog); + +G_END_DECLS + +#endif /* TERMINAL_SEARCH_DIALOG_H */ diff --git a/src/terminal-tab-label.c b/src/terminal-tab-label.c new file mode 100644 index 0000000..3a28504 --- /dev/null +++ b/src/terminal-tab-label.c @@ -0,0 +1,289 @@ +/* + * Copyright © 2001 Havoc Pennington + * Copyright © 2007, 2008 Christian Persch + * + * 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 3, or (at your option) + * any later version. + * + * This program is distributed in the hope tab_label 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 <gtk/gtk.h> + +#include "terminal-intl.h" +#include "terminal-tab-label.h" + +#define TERMINAL_TAB_LABEL_GET_PRIVATE(tab_label)(G_TYPE_INSTANCE_GET_PRIVATE ((tab_label), TERMINAL_TYPE_TAB_LABEL, TerminalTabLabelPrivate)) + +#define SPACING (4) + +struct _TerminalTabLabelPrivate +{ + TerminalScreen *screen; + GtkWidget *label; + GtkWidget *close_button; + gboolean bold; +}; + +enum +{ + PROP_0, + PROP_SCREEN +}; + +enum +{ + CLOSE_BUTTON_CLICKED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (TerminalTabLabel, terminal_tab_label, GTK_TYPE_HBOX); + +/* helper functions */ + +static void +close_button_clicked_cb (GtkWidget *widget, + TerminalTabLabel *tab_label) +{ + g_signal_emit (tab_label, signals[CLOSE_BUTTON_CLICKED], 0); +} + +static void +sync_tab_label (TerminalScreen *screen, + GParamSpec *pspec, + GtkWidget *label) +{ + GtkWidget *hbox; + const char *title; + + title = terminal_screen_get_title (screen); + hbox = gtk_widget_get_parent (label); + + gtk_label_set_text (GTK_LABEL (label), title); + + gtk_widget_set_tooltip_text (hbox, title); +} + +/* public functions */ + +/* Class implementation */ + +static void +terminal_tab_label_parent_set (GtkWidget *widget, + GtkWidget *old_parent) +{ + void (* parent_set) (GtkWidget *, GtkWidget *) = GTK_WIDGET_CLASS (terminal_tab_label_parent_class)->parent_set; + + if (parent_set) + parent_set (widget, old_parent); +} + +static void +terminal_tab_label_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + TerminalTabLabel *tab_label = TERMINAL_TAB_LABEL (widget); + TerminalTabLabelPrivate *priv = tab_label->priv; + void (* style_set) (GtkWidget *, GtkStyle *) = GTK_WIDGET_CLASS (terminal_tab_label_parent_class)->style_set; + int h, w; + + if (style_set) + style_set (widget, previous_style); + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget), + GTK_ICON_SIZE_MENU, &w, &h); + gtk_widget_set_size_request (priv->close_button, w + 2, h + 2); +} + +static void +terminal_tab_label_init (TerminalTabLabel *tab_label) +{ + tab_label->priv = TERMINAL_TAB_LABEL_GET_PRIVATE (tab_label); +} + +static GObject * +terminal_tab_label_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) +{ + GObject *object; + TerminalTabLabel *tab_label; + TerminalTabLabelPrivate *priv; + GtkWidget *hbox, *label, *close_button, *image; + + object = G_OBJECT_CLASS (terminal_tab_label_parent_class)->constructor + (type, n_construct_properties, construct_params); + + tab_label = TERMINAL_TAB_LABEL (object); + hbox = GTK_WIDGET (tab_label); + priv = tab_label->priv; + + g_assert (priv->screen != NULL); + + gtk_box_set_spacing (GTK_BOX (hbox), SPACING); + + priv->label = label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_misc_set_padding (GTK_MISC (label), 0, 0); + gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); + gtk_label_set_single_line_mode (GTK_LABEL (label), TRUE); + + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0); + + priv->close_button = close_button = gtk_button_new (); + gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE); + gtk_button_set_focus_on_click (GTK_BUTTON (close_button), FALSE); + gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE); + gtk_widget_set_name (close_button, "mate-terminal-tab-close-button"); + gtk_widget_set_tooltip_text (close_button, _("Close tab")); + + image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); + gtk_container_add (GTK_CONTAINER (close_button), image); + gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0); + + sync_tab_label (priv->screen, NULL, label); + g_signal_connect (priv->screen, "notify::title", + G_CALLBACK (sync_tab_label), label); + + g_signal_connect (close_button, "clicked", + G_CALLBACK (close_button_clicked_cb), tab_label); + + gtk_widget_show_all (hbox); + + return object; +} + +static void +terminal_tab_label_finalize (GObject *object) +{ +// TerminalTabLabel *tab_label = TERMINAL_TAB_LABEL (object); + + G_OBJECT_CLASS (terminal_tab_label_parent_class)->finalize (object); +} + +static void +terminal_tab_label_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TerminalTabLabel *tab_label = TERMINAL_TAB_LABEL (object); + TerminalTabLabelPrivate *priv = tab_label->priv; + + switch (prop_id) { + case PROP_SCREEN: + priv->screen = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_tab_label_class_init (TerminalTabLabelClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gobject_class->constructor = terminal_tab_label_constructor; + gobject_class->finalize = terminal_tab_label_finalize; + gobject_class->set_property = terminal_tab_label_set_property; + + widget_class->parent_set = terminal_tab_label_parent_set; + widget_class->style_set = terminal_tab_label_style_set; + + signals[CLOSE_BUTTON_CLICKED] = + g_signal_new (I_("close-button-clicked"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (TerminalTabLabelClass, close_button_clicked), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_object_class_install_property + (gobject_class, + PROP_SCREEN, + g_param_spec_object ("screen", NULL, NULL, + TERMINAL_TYPE_SCREEN, + G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | + G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (gobject_class, sizeof (TerminalTabLabelPrivate)); +} + +/* public API */ + +/** + * terminal_tab_label_new: + * @screen: a #TerminalScreen + * + * Returns: a new #TerminalTabLabel for @screen + */ +GtkWidget * +terminal_tab_label_new (TerminalScreen *screen) +{ + return g_object_new (TERMINAL_TYPE_TAB_LABEL, + "screen", screen, + NULL); +} + +/** + * terminal_tab_label_set_bold: + * @tab_label: a #TerminalTabLabel + * @bold: whether to enable label bolding + * + * Sets the tab label text bold, or unbolds it. + */ +void +terminal_tab_label_set_bold (TerminalTabLabel *tab_label, + gboolean bold) +{ + TerminalTabLabelPrivate *priv = tab_label->priv; + PangoAttrList *attr_list; + PangoAttribute *weight_attr; + gboolean free_list = FALSE; + + bold = bold != FALSE; + if (priv->bold == bold) + return; + + priv->bold = bold; + + attr_list = gtk_label_get_attributes (GTK_LABEL (priv->label)); + if (!attr_list) { + attr_list = pango_attr_list_new (); + free_list = TRUE; + } + + if (bold) + weight_attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); + else + weight_attr = pango_attr_weight_new (PANGO_WEIGHT_NORMAL); + + /* gtk_label_get_attributes() returns the label's internal list, + * which we're probably not supposed to modify directly. + * It seems to work ok however. + */ + pango_attr_list_change (attr_list, weight_attr); + + gtk_label_set_attributes (GTK_LABEL (priv->label), attr_list); + + if (free_list) + pango_attr_list_unref (attr_list); +} diff --git a/src/terminal-tab-label.h b/src/terminal-tab-label.h new file mode 100644 index 0000000..d025e24 --- /dev/null +++ b/src/terminal-tab-label.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2008 Christian Persch + * + * 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 3, or (at your option) + * any later version. + * + * This program is distributed in the hope tab_label 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 TERMINAL_TAB_LABEL_H +#define TERMINAL_TAB_LABEL_H + +#include <gtk/gtk.h> + +#include "terminal-screen.h" + +G_BEGIN_DECLS + +#define TERMINAL_TYPE_TAB_LABEL (terminal_tab_label_get_type ()) +#define TERMINAL_TAB_LABEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TERMINAL_TYPE_TAB_LABEL, TerminalTabLabel)) +#define TERMINAL_TAB_LABEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TERMINAL_TYPE_TAB_LABEL, TerminalTabLabelClass)) +#define TERMINAL_IS_TAB_LABEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TERMINAL_TYPE_TAB_LABEL)) +#define TERMINAL_IS_TAB_LABEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TERMINAL_TYPE_TAB_LABEL)) +#define TERMINAL_TAB_LABEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TERMINAL_TYPE_TAB_LABEL, TerminalTabLabelClass)) + +typedef struct _TerminalTabLabel TerminalTabLabel; +typedef struct _TerminalTabLabelClass TerminalTabLabelClass; +typedef struct _TerminalTabLabelPrivate TerminalTabLabelPrivate; + +struct _TerminalTabLabel +{ + GtkHBox parent_instance; + + /*< private >*/ + TerminalTabLabelPrivate *priv; +}; + +struct _TerminalTabLabelClass +{ + GtkHBoxClass parent_class; + + /* Signals */ + void (* close_button_clicked) (TerminalTabLabel *tab_label); +}; + +GType terminal_tab_label_get_type (void); + +GtkWidget *terminal_tab_label_new (TerminalScreen *screen); + +void terminal_tab_label_set_bold (TerminalTabLabel *tab_label, + gboolean bold); + +G_END_DECLS + +#endif /* !TERMINAL_TAB_LABEL_H */ diff --git a/src/terminal-tabs-menu.c b/src/terminal-tabs-menu.c new file mode 100644 index 0000000..2638a28 --- /dev/null +++ b/src/terminal-tabs-menu.c @@ -0,0 +1,487 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright © 2003 David Bordoley + * Copyright © 2003-2004 Christian Persch + * + * 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 3, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include <gtk/gtk.h> + +#include "terminal-tabs-menu.h" +#include "terminal-screen.h" +#include "terminal-screen-container.h" +#include "terminal-intl.h" + +#define TERMINAL_ACCELS_N_TABS_SWITCH (12) + +#define LABEL_WIDTH_CHARS 32 +#define ACTION_VERB_FORMAT_PREFIX "JmpTab" +#define ACTION_VERB_FORMAT_PREFIX_LEN strlen (ACTION_VERB_FORMAT_PREFIX) +#define ACTION_VERB_FORMAT ACTION_VERB_FORMAT_PREFIX "%x" +#define ACTION_VERB_FORMAT_LENGTH strlen (ACTION_VERB_FORMAT) + 14 + 1 +#define ACTION_VERB_FORMAT_BASE (16) /* %x is hex */ +#define ACCEL_PATH_FORMAT "<Actions>/Main/TabsSwitch%u" +#define ACCEL_PATH_FORMAT_LENGTH strlen (ACCEL_PATH_FORMAT) + 14 + 1 +#define DATA_KEY "TerminalTabsMenu::Action" + +#define UI_PATH "/menubar/Tabs" + +#define TERMINAL_TABS_MENU_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), TERMINAL_TYPE_TABS_MENU, TerminalTabsMenuPrivate)) + +struct _TerminalTabsMenuPrivate +{ + TerminalWindow *window; + GtkActionGroup *action_group; + GtkAction *anchor_action; + guint ui_id; +}; + +enum +{ + PROP_0, + PROP_WINDOW +}; + +static void terminal_tabs_menu_update (TerminalTabsMenu *menu); + +/* FIXME: this can be severely optimised */ +static GByteArray *tabs_id_array = NULL; +static guint n_tabs = 0; + +G_DEFINE_TYPE (TerminalTabsMenu, terminal_tabs_menu, G_TYPE_OBJECT) + +/* We need to assign unique IDs to tabs, otherwise accels get confused in the + * tabs menu (bug #339548). We could use a serial #, but the ID is used in the + * action name which is stored in a GQuark and so we should allocate them + * efficiently. + */ +static guint +allocate_tab_id (void) +{ + int bit; + guint b, len; + guint8 *data; + guint8 byte, mask; + + if (n_tabs++ == 0) + { + g_assert (tabs_id_array == NULL); + tabs_id_array = g_byte_array_sized_new (16); + } + + /* Find a free ID */ + len = tabs_id_array->len; + data = tabs_id_array->data; + for (b = 0; b < len; ++b) + { + if (data[b] != 0xff) + break; + } + + /* Need to append a new byte */ + if (b == len) + { + guint8 bytes[] = { 0 }; + g_byte_array_append (tabs_id_array, bytes, G_N_ELEMENTS (bytes)); + g_assert (tabs_id_array->len > b); + } + + data = tabs_id_array->data + b; + byte = 0xff ^ *data; + /* Now find the first free bit */ + bit = g_bit_nth_lsf (byte, -1); + mask = 1 << bit; + g_assert (bit >= 0 && bit <= 7); + g_assert ((*data & mask) == 0); + /* And mark it as allocated */ + *data |= mask; + + return b * 8 + bit; +} + +static void +free_tab_id (GtkAction *action) +{ + const char *name; + guint id; + guint8 *data; + guint b, bit; + + name = gtk_action_get_name (action); + id = g_ascii_strtoull (name + ACTION_VERB_FORMAT_PREFIX_LEN, NULL, + ACTION_VERB_FORMAT_BASE); + g_assert (id < tabs_id_array->len * 8); + + b = id >> 3; + bit = id & 0x7; + data = tabs_id_array->data + b; + *data &= ~(1 << bit); + + g_assert (n_tabs > 0); + if (--n_tabs == 0) + { + g_assert (tabs_id_array != NULL); + g_byte_array_free (tabs_id_array, TRUE); + tabs_id_array = NULL; + } +} + +static void +tab_action_activate_cb (GtkToggleAction *action, + TerminalTabsMenu *menu) +{ + TerminalTabsMenuPrivate *priv = menu->priv; + TerminalScreen *screen; + + if (gtk_toggle_action_get_active (action) == FALSE) + { + return; + } + + screen = g_object_get_data (G_OBJECT (action), DATA_KEY); + g_return_if_fail (screen != NULL); + + if (terminal_window_get_active (priv->window) != screen) + { + terminal_window_switch_screen (priv->window, screen); + } +} + +static void +sync_tab_title (TerminalScreen *screen, + GParamSpec *pspec, + GtkAction *action) +{ + const char *title; + + title = terminal_screen_get_title (screen); + + g_object_set (action, "label", title, NULL); +} + +static void +notebook_page_added_cb (GtkNotebook *notebook, + TerminalScreenContainer *container, + guint position, + TerminalTabsMenu *menu) +{ + TerminalTabsMenuPrivate *priv = menu->priv; + GtkAction *action; + char verb[ACTION_VERB_FORMAT_LENGTH]; + GSList *group; + TerminalScreen *screen; + + screen = terminal_screen_container_get_screen (container); + + g_snprintf (verb, sizeof (verb), ACTION_VERB_FORMAT, allocate_tab_id ()); + + action = g_object_new (GTK_TYPE_RADIO_ACTION, + "name", verb, + "tooltip", _("Switch to this tab"), + NULL); + + sync_tab_title (screen, NULL, action); + /* make sure the action is alive when handling the signal, see bug #169833 */ + g_signal_connect_object (screen, "notify::title", + G_CALLBACK (sync_tab_title), action, 0); + + gtk_action_group_add_action_with_accel (priv->action_group, action, NULL); + + group = gtk_radio_action_get_group (GTK_RADIO_ACTION (priv->anchor_action)); + gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group); + + /* set this here too, since tab-added comes after notify::active-child */ + if (terminal_window_get_active (priv->window) == screen) + { + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + } + + g_object_set_data (G_OBJECT (screen), DATA_KEY, action); + g_object_set_data (G_OBJECT (action), DATA_KEY, screen); + + g_signal_connect (action, "activate", + G_CALLBACK (tab_action_activate_cb), menu); + + g_object_unref (action); + + terminal_tabs_menu_update (menu); +} + +static void +notebook_page_removed_cb (GtkNotebook *notebook, + TerminalScreenContainer *container, + guint position, + TerminalTabsMenu *menu) +{ + TerminalTabsMenuPrivate *priv = menu->priv; + GtkAction *action; + TerminalScreen *screen; + + screen = terminal_screen_container_get_screen (container); + + action = g_object_get_data (G_OBJECT (screen), DATA_KEY); + g_return_if_fail (action != NULL); + + free_tab_id (action); + + g_signal_handlers_disconnect_by_func + (screen, G_CALLBACK (sync_tab_title), action); + + g_signal_handlers_disconnect_by_func + (action, G_CALLBACK (tab_action_activate_cb), menu); + + g_object_set_data (G_OBJECT (screen), DATA_KEY, NULL); + gtk_action_group_remove_action (priv->action_group, action); + + terminal_tabs_menu_update (menu); +} + +static void +notebook_page_reordered_cb (GtkNotebook *notebook, + GtkBin *bin, + guint position, + TerminalTabsMenu *menu) +{ + terminal_tabs_menu_update (menu); +} + +static void +notebook_page_switch_cb (GtkNotebook *notebook, +#if GTK_CHECK_VERSION (2, 90, 6) + GtkWidget *page, +#else + gpointer page, +#endif + guint position, + TerminalTabsMenu *menu) +{ + TerminalScreenContainer *container; + TerminalScreen *screen; + GtkAction *action; + +#if GTK_CHECK_VERSION (2, 90, 6) + container = TERMINAL_SCREEN_CONTAINER (page); +#else + container = TERMINAL_SCREEN_CONTAINER (gtk_notebook_get_nth_page (notebook, position)); +#endif + screen = terminal_screen_container_get_screen (container); + + action = g_object_get_data (G_OBJECT (screen), DATA_KEY); + g_signal_handlers_block_by_func (action, G_CALLBACK (tab_action_activate_cb), menu); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + g_signal_handlers_unblock_by_func (action, G_CALLBACK (tab_action_activate_cb), menu); +} + +static void +connect_proxy_cb (GtkActionGroup *action_group, + GtkAction *action, + GtkWidget *proxy, + gpointer dummy) +{ + if (GTK_IS_MENU_ITEM (proxy)) + { + GtkLabel *label; + + label = GTK_LABEL (gtk_bin_get_child (GTK_BIN (proxy))); + + gtk_label_set_use_underline (label, FALSE); + gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END); + gtk_label_set_max_width_chars (label, LABEL_WIDTH_CHARS); + } +} + +static void +terminal_tabs_menu_set_window (TerminalTabsMenu *menu, + TerminalWindow *window) +{ + TerminalTabsMenuPrivate *priv = menu->priv; + GtkWidget *notebook; + GtkUIManager *manager; + + priv->window = window; + + manager = GTK_UI_MANAGER (terminal_window_get_ui_manager (window)); + priv->action_group = gtk_action_group_new ("TabsActions"); + gtk_ui_manager_insert_action_group (manager, priv->action_group, -1); + g_object_unref (priv->action_group); + + priv->anchor_action = g_object_new (GTK_TYPE_RADIO_ACTION, + "name", "TabsMenuAnchorAction", + NULL); + gtk_action_group_add_action (priv->action_group, priv->anchor_action); + g_object_unref (priv->anchor_action); + + g_signal_connect (priv->action_group, "connect-proxy", + G_CALLBACK (connect_proxy_cb), NULL); + + notebook = terminal_window_get_notebook (window); + g_signal_connect_object (notebook, "page-added", + G_CALLBACK (notebook_page_added_cb), menu, 0); + g_signal_connect_object (notebook, "page-removed", + G_CALLBACK (notebook_page_removed_cb), menu, 0); + g_signal_connect_object (notebook, "page-reordered", + G_CALLBACK (notebook_page_reordered_cb), menu, 0); + g_signal_connect_object (notebook, "switch-page", + G_CALLBACK (notebook_page_switch_cb), menu, 0); +} + +static void +terminal_tabs_menu_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TerminalTabsMenu *menu = TERMINAL_TABS_MENU (object); + + switch (prop_id) + { + case PROP_WINDOW: + terminal_tabs_menu_set_window (menu, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_tabs_menu_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + /* no readable properties */ + g_return_if_reached (); +} + +static void +terminal_tabs_menu_class_init (TerminalTabsMenuClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = terminal_tabs_menu_set_property; + object_class->get_property = terminal_tabs_menu_get_property; + + g_object_class_install_property (object_class, + PROP_WINDOW, + g_param_spec_object ("window", NULL, NULL, + TERMINAL_TYPE_WINDOW, + G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | + G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (object_class, sizeof (TerminalTabsMenuPrivate)); + + /* We don't want to save accels, so skip them */ + gtk_accel_map_add_filter ("<Actions>/Main/TabsSwitch*"); +} + +static void +terminal_tabs_menu_init (TerminalTabsMenu *menu) +{ + menu->priv = TERMINAL_TABS_MENU_GET_PRIVATE (menu); +} + +static void +terminal_tabs_menu_clean (TerminalTabsMenu *menu) +{ + TerminalTabsMenuPrivate *p = menu->priv; + GtkUIManager *manager = GTK_UI_MANAGER (terminal_window_get_ui_manager (p->window)); + + if (p->ui_id != 0) + { + gtk_ui_manager_remove_ui (manager, p->ui_id); + gtk_ui_manager_ensure_update (manager); + p->ui_id = 0; + } +} + +TerminalTabsMenu * +terminal_tabs_menu_new (TerminalWindow *window) +{ + return TERMINAL_TABS_MENU (g_object_new (TERMINAL_TYPE_TABS_MENU, + "window", window, + NULL)); +} + +static void +tab_set_action_accelerator (GtkActionGroup *action_group, + GtkAction *action, + guint tab_number, + gboolean is_single_tab) +{ + if (!is_single_tab && + tab_number < TERMINAL_ACCELS_N_TABS_SWITCH) + { + char accel_path[ACCEL_PATH_FORMAT_LENGTH]; + + g_snprintf (accel_path, sizeof (accel_path), ACCEL_PATH_FORMAT, tab_number + 1); + gtk_action_set_accel_path (action, accel_path); + } + else + { + gtk_action_set_accel_path (action, NULL); + return; + } +} + +static void +terminal_tabs_menu_update (TerminalTabsMenu *menu) +{ + TerminalTabsMenuPrivate *p = menu->priv; + GtkUIManager *manager; + GtkAction *action; + GList *tabs = NULL, *l; + guint i = 0, n; + gboolean is_single_tab; + const char *verb; + + terminal_tabs_menu_clean (menu); + + tabs = terminal_window_list_screen_containers (p->window); + + n = g_list_length (tabs); + if (n == 0) return; + + is_single_tab = (n == 1); + + manager = GTK_UI_MANAGER (terminal_window_get_ui_manager (p->window)); + p->ui_id = gtk_ui_manager_new_merge_id (manager); + + for (l = tabs; l != NULL; l = l->next) + { + TerminalScreenContainer *container = TERMINAL_SCREEN_CONTAINER (l->data); + GObject *screen = G_OBJECT (terminal_screen_container_get_screen (container)); + + action = g_object_get_data (screen, DATA_KEY); + g_return_if_fail (action != NULL); + + verb = gtk_action_get_name (action); + + tab_set_action_accelerator (p->action_group, action, i++, is_single_tab); + + gtk_ui_manager_add_ui (manager, p->ui_id, + UI_PATH, + verb, verb, + GTK_UI_MANAGER_MENUITEM, FALSE); + } + + g_list_free (tabs); +} diff --git a/src/terminal-tabs-menu.h b/src/terminal-tabs-menu.h new file mode 100644 index 0000000..6e609e5 --- /dev/null +++ b/src/terminal-tabs-menu.h @@ -0,0 +1,56 @@ +/* + * Copyright © 2003 David Bordoley + * + * 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 3, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TERMINAL_TABS_MENU_H +#define TERMINAL_TABS_MENU_H + +#include "terminal-window.h" + +G_BEGIN_DECLS + +#define TERMINAL_TYPE_TABS_MENU (terminal_tabs_menu_get_type ()) +#define TERMINAL_TABS_MENU(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TERMINAL_TYPE_TABS_MENU, TerminalTabsMenu)) +#define TERMINAL_TABS_MENU_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TERMINAL_TYPE_TABS_MENU, TerminalTabsMenuClass)) +#define TERMINAL_IS_TABS_MENU(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TERMINAL_TYPE_TABS_MENU)) +#define TERMINAL_IS_TABS_MENU_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TERMINAL_TYPE_TABS_MENU)) +#define TERMINAL_TABS_MENU_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TERMINAL_TYPE_TABS_MENU, TerminalTabsMenuClass)) + +typedef struct _TerminalTabsMenu TerminalTabsMenu; +typedef struct _TerminalTabsMenuClass TerminalTabsMenuClass; +typedef struct _TerminalTabsMenuPrivate TerminalTabsMenuPrivate; + +struct _TerminalTabsMenuClass +{ + GObjectClass parent_class; +}; + +struct _TerminalTabsMenu +{ + GObject parent_object; + + /*< private >*/ + TerminalTabsMenuPrivate *priv; +}; + +GType terminal_tabs_menu_get_type (void); + +TerminalTabsMenu *terminal_tabs_menu_new (TerminalWindow *window); + +G_END_DECLS + +#endif diff --git a/src/terminal-type-builtins.c.template b/src/terminal-type-builtins.c.template new file mode 100644 index 0000000..eb8fe99 --- /dev/null +++ b/src/terminal-type-builtins.c.template @@ -0,0 +1,45 @@ +/*** BEGIN file-header ***/ +#include <config.h> + +#include "terminal-type-builtins.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@filename@" */ + +#include "@filename@" + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + GType g_define_type_id = \ + g_@type@_register_static (/* g_intern_static_string */ ("@EnumName@"), values); + + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} + +/*** END value-tail ***/ + +/*** BEGIN file-tail ***/ + +/*** END file-tail ***/ diff --git a/src/terminal-type-builtins.h.template b/src/terminal-type-builtins.h.template new file mode 100644 index 0000000..c454d05 --- /dev/null +++ b/src/terminal-type-builtins.h.template @@ -0,0 +1,25 @@ +/*** BEGIN file-header ***/ +#ifndef TERMINAL_TYPE_BUILTINS_H +#define TERMINAL_TYPE_BUILTINS_H + +#include <glib-object.h> + +G_BEGIN_DECLS +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/* enumerations from "@filename@" */ +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType @enum_name@_get_type (void); +#define TERMINAL_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) +/*** END value-header ***/ + +/*** BEGIN file-tail ***/ + +G_END_DECLS + +#endif /* !TERMINAL_TYPE_BUILTINS_H */ +/*** END file-tail ***/ diff --git a/src/terminal-util.c b/src/terminal-util.c new file mode 100644 index 0000000..6bf6618 --- /dev/null +++ b/src/terminal-util.c @@ -0,0 +1,1213 @@ +/* + * Copyright © 2001, 2002 Havoc Pennington + * Copyright © 2002 Red Hat, Inc. + * Copyright © 2002 Sun Microsystems + * Copyright © 2003 Mariano Suarez-Alvarez + * Copyright © 2008 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <sys/types.h> + +#include <glib.h> + +#include <gio/gio.h> +#include <gtk/gtk.h> + +#include <mateconf/mateconf.h> + +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#include <X11/Xatom.h> +#endif + +#include "terminal-accels.h" +#include "terminal-app.h" +#include "terminal-intl.h" +#include "terminal-util.h" +#include "terminal-window.h" + +void +terminal_util_set_unique_role (GtkWindow *window, const char *prefix) +{ + char *role; + + role = g_strdup_printf ("%s-%d-%d-%d", prefix, getpid (), g_random_int (), (int) time (NULL)); + gtk_window_set_role (window, role); + g_free (role); +} + +/** + * terminal_util_show_error_dialog: + * @transient_parent: parent of the future dialog window; + * @weap_ptr: pointer to a #Widget pointer, to control the population. + * @error: a #GError, or %NULL + * @message_format: printf() style format string + * + * Create a #GtkMessageDialog window with the message, and present it, handling its buttons. + * If @weap_ptr is not #NULL, only create the dialog if <literal>*weap_ptr</literal> is #NULL + * (and in that * case, set @weap_ptr to be a weak pointer to the new dialog), otherwise just + * present <literal>*weak_ptr</literal>. Note that in this last case, the message <emph>will</emph> + * be changed. + */ +void +terminal_util_show_error_dialog (GtkWindow *transient_parent, + GtkWidget **weak_ptr, + GError *error, + const char *message_format, + ...) +{ + char *message; + va_list args; + + if (message_format) + { + va_start (args, message_format); + message = g_strdup_vprintf (message_format, args); + va_end (args); + } + else message = NULL; + + if (weak_ptr == NULL || *weak_ptr == NULL) + { + GtkWidget *dialog; + dialog = gtk_message_dialog_new (transient_parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + message ? "%s" : NULL, + message); + + if (error != NULL) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", error->message); + + g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (gtk_widget_destroy), NULL); + + if (weak_ptr != NULL) + { + *weak_ptr = dialog; + g_object_add_weak_pointer (G_OBJECT (dialog), (void**)weak_ptr); + } + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + gtk_widget_show_all (dialog); + } + else + { + g_return_if_fail (GTK_IS_MESSAGE_DIALOG (*weak_ptr)); + + /* Sucks that there's no direct accessor for "text" property */ + g_object_set (G_OBJECT (*weak_ptr), "text", message, NULL); + + gtk_window_present (GTK_WINDOW (*weak_ptr)); + } + + g_free (message); +} + +static gboolean +open_url (GtkWindow *parent, + const char *uri, + guint32 user_time, + GError **error) +{ + GdkScreen *screen; + + if (parent) + screen = gtk_widget_get_screen (GTK_WIDGET (parent)); + else + screen = gdk_screen_get_default (); + + return gtk_show_uri (screen, uri, user_time, error); +} + +void +terminal_util_show_help (const char *topic, + GtkWindow *parent) +{ + GError *error = NULL; + const char *lang; + char *uri = NULL, *url; + guint i; + + const char * const * langs = g_get_language_names (); + for (i = 0; langs[i]; i++) { + lang = langs[i]; + if (strchr (lang, '.')) { + continue; + } + + uri = g_build_filename (TERM_HELPDIR, + "mate-terminal", /* DOC_MODULE */ + lang, + "mate-terminal.xml", + NULL); + + if (g_file_test (uri, G_FILE_TEST_EXISTS)) { + break; + } + + g_free (uri); + uri = NULL; + } + + if (!uri) + return; + + if (topic) { + url = g_strdup_printf ("ghelp://%s?%s", uri, topic); + } else { + url = g_strdup_printf ("ghelp://%s", uri); + } + + if (!open_url (GTK_WINDOW (parent), url, gtk_get_current_event_time (), &error)) + { + terminal_util_show_error_dialog (GTK_WINDOW (parent), NULL, error, + _("There was an error displaying help")); + g_error_free (error); + } + + g_free (uri); + g_free (url); +} + +/* sets accessible name and description for the widget */ + +void +terminal_util_set_atk_name_description (GtkWidget *widget, + const char *name, + const char *desc) +{ + AtkObject *obj; + + obj = gtk_widget_get_accessible (widget); + + if (obj == NULL) + { + g_warning ("%s: for some reason widget has no GtkAccessible", + G_STRFUNC); + return; + } + + + if (!GTK_IS_ACCESSIBLE (obj)) + return; /* This means GAIL is not loaded so we have the NoOp accessible */ + + g_return_if_fail (GTK_IS_ACCESSIBLE (obj)); + if (desc) + atk_object_set_description (obj, desc); + if (name) + atk_object_set_name (obj, name); +} + +void +terminal_util_open_url (GtkWidget *parent, + const char *orig_url, + TerminalURLFlavour flavor, + guint32 user_time) +{ + GError *error = NULL; + char *uri; + + g_return_if_fail (orig_url != NULL); + + switch (flavor) + { + case FLAVOR_DEFAULT_TO_HTTP: + uri = g_strdup_printf ("http:%s", orig_url); + break; + case FLAVOR_EMAIL: + if (g_ascii_strncasecmp ("mailto:", orig_url, 7) != 0) + uri = g_strdup_printf ("mailto:%s", orig_url); + else + uri = g_strdup (orig_url); + break; + case FLAVOR_VOIP_CALL: + case FLAVOR_AS_IS: + uri = g_strdup (orig_url); + break; + case FLAVOR_SKEY: + /* shouldn't get this */ + default: + uri = NULL; + g_assert_not_reached (); + } + + if (!open_url (GTK_WINDOW (parent), uri, user_time, &error)) + { + terminal_util_show_error_dialog (GTK_WINDOW (parent), NULL, error, + _("Could not open the address “%s”"), + uri); + + g_error_free (error); + } + + g_free (uri); +} + +/** + * terminal_util_resolve_relative_path: + * @path: + * @relative_path: + * + * Returns: a newly allocate string + */ +char * +terminal_util_resolve_relative_path (const char *path, + const char *relative_path) +{ + GFile *file, *resolved_file; + char *resolved_path = NULL; + + g_return_val_if_fail (relative_path != NULL, NULL); + + if (path == NULL) + return g_strdup (relative_path); + + file = g_file_new_for_path (path); + resolved_file = g_file_resolve_relative_path (file, relative_path); + g_object_unref (file); + + if (resolved_file == NULL) + return NULL; + + resolved_path = g_file_get_path (resolved_file); + g_object_unref (resolved_file); + + return resolved_path; +} + +/** + * terminal_util_transform_uris_to_quoted_fuse_paths: + * @uris: + * + * Transforms those URIs in @uris to shell-quoted paths that point to + * GIO fuse paths. + */ +void +terminal_util_transform_uris_to_quoted_fuse_paths (char **uris) +{ + guint i; + + if (!uris) + return; + + for (i = 0; uris[i]; ++i) + { + GFile *file; + char *path; + + file = g_file_new_for_uri (uris[i]); + + if ((path = g_file_get_path (file))) + { + char *quoted; + + quoted = g_shell_quote (path); + g_free (uris[i]); + g_free (path); + + uris[i] = quoted; + } + + g_object_unref (file); + } +} + +char * +terminal_util_concat_uris (char **uris, + gsize *length) +{ + GString *string; + gsize len; + guint i; + + len = 0; + for (i = 0; uris[i]; ++i) + len += strlen (uris[i]) + 1; + + if (length) + *length = len; + + string = g_string_sized_new (len + 1); + for (i = 0; uris[i]; ++i) + { + g_string_append (string, uris[i]); + g_string_append_c (string, ' '); + } + + return g_string_free (string, FALSE); +} + +char * +terminal_util_get_licence_text (void) +{ + const gchar *license[] = { + N_("MATE Terminal 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 3 of the License, or " + "(at your option) any later version."), + N_("MATE Terminal 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."), + N_("You should have received a copy of the GNU General Public License " + "along with MATE Terminal; if not, write to the Free Software Foundation, " + "Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA") + }; + + return g_strjoin ("\n\n", _(license[0]), _(license[1]), _(license[2]), NULL); +} + +gboolean +terminal_util_load_builder_file (const char *filename, + const char *object_name, + ...) +{ + char *path; + GtkBuilder *builder; + GError *error = NULL; + va_list args; + + path = g_build_filename (TERM_PKGDATADIR, filename, NULL); + builder = gtk_builder_new (); + if (!gtk_builder_add_from_file (builder, path, &error)) { + g_warning ("Failed to load %s: %s\n", filename, error->message); + g_error_free (error); + g_free (path); + g_object_unref (builder); + return FALSE; + } + g_free (path); + + va_start (args, object_name); + + while (object_name) { + GObject **objectptr; + + objectptr = va_arg (args, GObject**); + *objectptr = gtk_builder_get_object (builder, object_name); + if (!*objectptr) { + g_warning ("Failed to fetch object \"%s\"\n", object_name); + break; + } + + object_name = va_arg (args, const char*); + } + + va_end (args); + + g_object_unref (builder); + return object_name == NULL; +} + +gboolean +terminal_util_dialog_response_on_delete (GtkWindow *widget) +{ + gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_DELETE_EVENT); + return TRUE; +} + +/* Like g_key_file_set_string, but escapes characters so that + * the stored string is ASCII. Use when the input string may not + * be UTF-8. + */ +void +terminal_util_key_file_set_string_escape (GKeyFile *key_file, + const char *group, + const char *key, + const char *string) +{ + char *escaped; + + /* FIXMEchpe: be more intelligent and only escape characters that aren't UTF-8 */ + escaped = g_strescape (string, NULL); + g_key_file_set_string (key_file, group, key, escaped); + g_free (escaped); +} + +char * +terminal_util_key_file_get_string_unescape (GKeyFile *key_file, + const char *group, + const char *key, + GError **error) +{ + char *escaped, *unescaped; + + escaped = g_key_file_get_string (key_file, group, key, error); + if (!escaped) + return NULL; + + unescaped = g_strcompress (escaped); + g_free (escaped); + + return unescaped; +} + +void +terminal_util_key_file_set_argv (GKeyFile *key_file, + const char *group, + const char *key, + int argc, + char **argv) +{ + char **quoted_argv; + char *flat; + int i; + + if (argc < 0) + argc = g_strv_length (argv); + + quoted_argv = g_new (char*, argc + 1); + for (i = 0; i < argc; ++i) + quoted_argv[i] = g_shell_quote (argv[i]); + quoted_argv[argc] = NULL; + + flat = g_strjoinv (" ", quoted_argv); + terminal_util_key_file_set_string_escape (key_file, group, key, flat); + + g_free (flat); + g_strfreev (quoted_argv); +} + +char ** +terminal_util_key_file_get_argv (GKeyFile *key_file, + const char *group, + const char *key, + int *argc, + GError **error) +{ + char **argv; + char *flat; + gboolean retval; + + flat = terminal_util_key_file_get_string_unescape (key_file, group, key, error); + if (!flat) + return NULL; + + retval = g_shell_parse_argv (flat, argc, &argv, error); + g_free (flat); + + if (retval) + return argv; + + return NULL; +} + +/* Proxy stuff */ + +static char * +conf_get_string (MateConfClient *conf, + const char *key) +{ + char *value; + value = mateconf_client_get_string (conf, key, NULL); + if (G_UNLIKELY (value && *value == '\0')) + { + g_free (value); + value = NULL; + } + return value; +} + +/* + * set_proxy_env: + * @env_table: a #GHashTable + * @key: the env var name + * @value: the env var value + * + * Adds @value for @key to @env_table, taking care to never overwrite an + * existing value for @key. @value is consumed. + */ +static void +set_proxy_env (GHashTable *env_table, + const char *key, + char *value) +{ + char *key1 = NULL, *key2 = NULL; + char *value1 = NULL, *value2 = NULL; + + if (!value) + return; + + if (g_hash_table_lookup (env_table, key) == NULL) + key1 = g_strdup (key); + + key2 = g_ascii_strup (key, -1); + if (g_hash_table_lookup (env_table, key) != NULL) + { + g_free (key2); + key2 = NULL; + } + + if (key1 && key2) + { + value1 = value; + value2 = g_strdup (value); + } + else if (key1) + value1 = value; + else if (key2) + value2 = value; + else + g_free (value); + + if (key1) + g_hash_table_replace (env_table, key1, value1); + if (key2) + g_hash_table_replace (env_table, key2, value2); +} + +static void +setup_http_proxy_env (GHashTable *env_table, + MateConfClient *conf) +{ + gchar *host; + gint port; + GSList *ignore; + + if (!mateconf_client_get_bool (conf, CONF_HTTP_PROXY_PREFIX "/use_http_proxy", NULL)) + return; + + host = conf_get_string (conf, CONF_HTTP_PROXY_PREFIX "/host"); + port = mateconf_client_get_int (conf, CONF_HTTP_PROXY_PREFIX "/port", NULL); + if (host && port) + { + GString *buf = g_string_sized_new (64); + g_string_append (buf, "http://"); + + if (mateconf_client_get_bool (conf, CONF_HTTP_PROXY_PREFIX "/use_authentication", NULL)) + { + char *user, *password; + user = conf_get_string (conf, CONF_HTTP_PROXY_PREFIX "/authentication_user"); + if (user) + { + g_string_append_uri_escaped (buf, user, NULL, TRUE); + password = conf_get_string (conf, CONF_HTTP_PROXY_PREFIX "/authentication_password"); + if (password) + { + g_string_append_c (buf, ':'); + g_string_append_uri_escaped (buf, password, NULL, TRUE); + g_free (password); + } + g_free (user); + g_string_append_c (buf, '@'); + } + } + g_string_append_printf (buf, "%s:%d/", host, port); + set_proxy_env (env_table, "http_proxy", g_string_free (buf, FALSE)); + } + g_free (host); + + ignore = mateconf_client_get_list (conf, CONF_HTTP_PROXY_PREFIX "/ignore_hosts", MATECONF_VALUE_STRING, NULL); + if (ignore) + { + GString *buf = g_string_sized_new (64); + while (ignore != NULL) + { + GSList *old; + + if (buf->len) + g_string_append_c (buf, ','); + g_string_append (buf, ignore->data); + + old = ignore; + ignore = g_slist_next (ignore); + g_free (old->data); + g_slist_free_1 (old); + } + set_proxy_env (env_table, "no_proxy", g_string_free (buf, FALSE)); + } +} + +static void +setup_https_proxy_env (GHashTable *env_table, + MateConfClient *conf) +{ + gchar *host; + gint port; + + host = conf_get_string (conf, CONF_PROXY_PREFIX "/secure_host"); + port = mateconf_client_get_int (conf, CONF_PROXY_PREFIX "/secure_port", NULL); + if (host && port) + { + char *proxy; + /* Even though it's https, the proxy scheme is 'http'. See bug #624440. */ + proxy = g_strdup_printf ("http://%s:%d/", host, port); + set_proxy_env (env_table, "https_proxy", proxy); + } + g_free (host); +} + +static void +setup_ftp_proxy_env (GHashTable *env_table, + MateConfClient *conf) +{ + gchar *host; + gint port; + + host = conf_get_string (conf, CONF_PROXY_PREFIX "/ftp_host"); + port = mateconf_client_get_int (conf, CONF_PROXY_PREFIX "/ftp_port", NULL); + if (host && port) + { + char *proxy; + /* Even though it's ftp, the proxy scheme is 'http'. See bug #624440. */ + proxy = g_strdup_printf ("http://%s:%d/", host, port); + set_proxy_env (env_table, "ftp_proxy", proxy); + } + g_free (host); +} + +static void +setup_socks_proxy_env (GHashTable *env_table, + MateConfClient *conf) +{ + gchar *host; + gint port; + + host = conf_get_string (conf, CONF_PROXY_PREFIX "/socks_host"); + port = mateconf_client_get_int (conf, CONF_PROXY_PREFIX "/socks_port", NULL); + if (host && port) + { + char *proxy; + proxy = g_strdup_printf ("socks://%s:%d/", host, port); + set_proxy_env (env_table, "all_proxy", proxy); + } + g_free (host); +} + +static void +setup_autoconfig_proxy_env (GHashTable *env_table, + MateConfClient *conf) +{ + /* XXX Not sure what to do with this. See bug #596688. + gchar *url; + + url = conf_get_string (conf, CONF_PROXY_PREFIX "/autoconfig_url"); + if (url) + { + char *proxy; + proxy = g_strdup_printf ("pac+%s", url); + set_proxy_env (env_table, "http_proxy", proxy); + } + g_free (url); + */ +} + +/** + * terminal_util_add_proxy_env: + * @env_table: a #GHashTable + * + * Adds the proxy env variables to @env_table. + */ +void +terminal_util_add_proxy_env (GHashTable *env_table) +{ + char *proxymode; + + MateConfClient *conf; + conf = mateconf_client_get_default (); + + /* If mode is not manual, nothing to set */ + proxymode = conf_get_string (conf, CONF_PROXY_PREFIX "/mode"); + if (proxymode && 0 == strcmp (proxymode, "manual")) + { + setup_http_proxy_env (env_table, conf); + setup_https_proxy_env (env_table, conf); + setup_ftp_proxy_env (env_table, conf); + setup_socks_proxy_env (env_table, conf); + } + else if (proxymode && 0 == strcmp (proxymode, "auto")) + { + setup_autoconfig_proxy_env (env_table, conf); + } + + g_free (proxymode); + g_object_unref (conf); +} + +/* Bidirectional object/widget binding */ + +typedef struct { + GObject *object; + const char *object_prop; + GtkWidget *widget; + gulong object_notify_id; + gulong widget_notify_id; + PropertyChangeFlags flags; +} PropertyChange; + +static void +property_change_free (PropertyChange *change) +{ + g_signal_handler_disconnect (change->object, change->object_notify_id); + + g_slice_free (PropertyChange, change); +} + +static gboolean +transform_boolean (gboolean input, + PropertyChangeFlags flags) +{ + if (flags & FLAG_INVERT_BOOL) + input = !input; + + return input; +} + +static void +object_change_notify_cb (PropertyChange *change) +{ + GObject *object = change->object; + const char *object_prop = change->object_prop; + GtkWidget *widget = change->widget; + + g_signal_handler_block (widget, change->widget_notify_id); + + if (GTK_IS_RADIO_BUTTON (widget)) + { + int ovalue, rvalue; + + g_object_get (object, object_prop, &ovalue, NULL); + rvalue = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "enum-value")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), ovalue == rvalue); + } + else if (GTK_IS_TOGGLE_BUTTON (widget)) + { + gboolean enabled; + + g_object_get (object, object_prop, &enabled, NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), + transform_boolean (enabled, change->flags)); + } + else if (GTK_IS_SPIN_BUTTON (widget)) + { + int value; + + g_object_get (object, object_prop, &value, NULL); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value); + } + else if (GTK_IS_ENTRY (widget)) + { + char *text; + + g_object_get (object, object_prop, &text, NULL); + gtk_entry_set_text (GTK_ENTRY (widget), text ? text : ""); + g_free (text); + } + else if (GTK_IS_COMBO_BOX (widget)) + { + int value; + + g_object_get (object, object_prop, &value, NULL); + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), value); + } + else if (GTK_IS_RANGE (widget)) + { + double value; + + g_object_get (object, object_prop, &value, NULL); + gtk_range_set_value (GTK_RANGE (widget), value); + } + else if (GTK_IS_COLOR_BUTTON (widget)) + { + GdkColor *color; + GdkColor old_color; + + g_object_get (object, object_prop, &color, NULL); + gtk_color_button_get_color (GTK_COLOR_BUTTON (widget), &old_color); + + if (color && !gdk_color_equal (color, &old_color)) + gtk_color_button_set_color (GTK_COLOR_BUTTON (widget), color); + if (color) + gdk_color_free (color); + } + else if (GTK_IS_FONT_BUTTON (widget)) + { + PangoFontDescription *font_desc; + char *font; + + g_object_get (object, object_prop, &font_desc, NULL); + if (!font_desc) + goto out; + + font = pango_font_description_to_string (font_desc); + gtk_font_button_set_font_name (GTK_FONT_BUTTON (widget), font); + g_free (font); + pango_font_description_free (font_desc); + } + else if (GTK_IS_FILE_CHOOSER (widget)) + { + char *name = NULL, *filename = NULL; + + g_object_get (object, object_prop, &name, NULL); + if (name) + filename = g_filename_from_utf8 (name, -1, NULL, NULL, NULL); + + if (filename) + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget), filename); + else + gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (widget)); + g_free (filename); + g_free (name); + } + +out: + g_signal_handler_unblock (widget, change->widget_notify_id); +} + +static void +widget_change_notify_cb (PropertyChange *change) +{ + GObject *object = change->object; + const char *object_prop = change->object_prop; + GtkWidget *widget = change->widget; + + g_signal_handler_block (change->object, change->object_notify_id); + + if (GTK_IS_RADIO_BUTTON (widget)) + { + gboolean active; + int value; + + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + if (!active) + goto out; + + value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "enum-value")); + g_object_set (object, object_prop, value, NULL); + } + else if (GTK_IS_TOGGLE_BUTTON (widget)) + { + gboolean enabled; + + enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + g_object_set (object, object_prop, transform_boolean (enabled, change->flags), NULL); + } + else if (GTK_IS_SPIN_BUTTON (widget)) + { + int value; + + value = (int) gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget)); + g_object_set (object, object_prop, value, NULL); + } + else if (GTK_IS_ENTRY (widget)) + { + const char *text; + + text = gtk_entry_get_text (GTK_ENTRY (widget)); + g_object_set (object, object_prop, text, NULL); + } + else if (GTK_IS_COMBO_BOX (widget)) + { + int value; + + value = gtk_combo_box_get_active (GTK_COMBO_BOX (widget)); + g_object_set (object, object_prop, value, NULL); + } + else if (GTK_IS_COLOR_BUTTON (widget)) + { + GdkColor color; + + gtk_color_button_get_color (GTK_COLOR_BUTTON (widget), &color); + g_object_set (object, object_prop, &color, NULL); + } + else if (GTK_IS_FONT_BUTTON (widget)) + { + PangoFontDescription *font_desc; + const char *font; + + font = gtk_font_button_get_font_name (GTK_FONT_BUTTON (widget)); + font_desc = pango_font_description_from_string (font); + g_object_set (object, object_prop, font_desc, NULL); + pango_font_description_free (font_desc); + } + else if (GTK_IS_RANGE (widget)) + { + double value; + + value = gtk_range_get_value (GTK_RANGE (widget)); + g_object_set (object, object_prop, value, NULL); + } + else if (GTK_IS_FILE_CHOOSER (widget)) + { + char *filename, *name = NULL; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget)); + if (filename) + name = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL); + + g_object_set (object, object_prop, name, NULL); + g_free (filename); + g_free (name); + } + +out: + g_signal_handler_unblock (change->object, change->object_notify_id); +} + +void +terminal_util_bind_object_property_to_widget (GObject *object, + const char *object_prop, + GtkWidget *widget, + PropertyChangeFlags flags) +{ + PropertyChange *change; + const char *signal_name; + char notify_signal_name[64]; + + change = g_slice_new0 (PropertyChange); + + change->widget = widget; + g_assert (g_object_get_data (G_OBJECT (widget), "GT:PCD") == NULL); + g_object_set_data_full (G_OBJECT (widget), "GT:PCD", change, (GDestroyNotify) property_change_free); + + if (GTK_IS_TOGGLE_BUTTON (widget)) + signal_name = "notify::active"; + else if (GTK_IS_SPIN_BUTTON (widget)) + signal_name = "notify::value"; + else if (GTK_IS_ENTRY (widget)) + signal_name = "notify::text"; + else if (GTK_IS_COMBO_BOX (widget)) + signal_name = "notify::active"; + else if (GTK_IS_COLOR_BUTTON (widget)) + signal_name = "notify::color"; + else if (GTK_IS_FONT_BUTTON (widget)) + signal_name = "notify::font-name"; + else if (GTK_IS_RANGE (widget)) + signal_name = "value-changed"; + else if (GTK_IS_FILE_CHOOSER_BUTTON (widget)) + signal_name = "file-set"; + else if (GTK_IS_FILE_CHOOSER (widget)) + signal_name = "selection-changed"; + else + g_assert_not_reached (); + + change->widget_notify_id = g_signal_connect_swapped (widget, signal_name, G_CALLBACK (widget_change_notify_cb), change); + + change->object = object; + change->flags = flags; + change->object_prop = object_prop; + + g_snprintf (notify_signal_name, sizeof (notify_signal_name), "notify::%s", object_prop); + object_change_notify_cb (change); + change->object_notify_id = g_signal_connect_swapped (object, notify_signal_name, G_CALLBACK (object_change_notify_cb), change); +} + +#ifdef GDK_WINDOWING_X11 + +/* We don't want to hop desktops when we unrealize/realize. + * So we need to save and restore the value of NET_WM_DESKTOP. This isn't + * exposed through GDK. + */ +gboolean +terminal_util_x11_get_net_wm_desktop (GdkWindow *window, + guint32 *desktop) +{ + GdkDisplay *display = gdk_drawable_get_display (window); + Atom type; + int format; + guchar *data; + gulong n_items, bytes_after; + gboolean result = FALSE; + + if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_DRAWABLE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_DESKTOP"), + 0, G_MAXLONG, False, AnyPropertyType, + &type, &format, &n_items, &bytes_after, &data) == Success && + type != None) + { + if (type == XA_CARDINAL && format == 32 && n_items == 1) + { + *desktop = *(gulong *)data; + result = TRUE; + } + + XFree (data); + } + + return result; +} + +void +terminal_util_x11_set_net_wm_desktop (GdkWindow *window, + guint32 desktop) +{ + /* We can't change the current desktop before mapping our window, + * because GDK has the annoying habit of clearing _NET_WM_DESKTOP + * before mapping a GdkWindow, So we we have to do it after instead. + * + * However, doing it after is different whether or not we have a + * window manager (if we don't have a window manager, we have to + * set the _NET_WM_DESKTOP property so that it picks it up when + * it starts) + * + * http://bugzilla.mate.org/show_bug.cgi?id=586311 asks for GTK+ + * to just handle everything behind the scenes including the desktop. + */ + GdkScreen *screen = gdk_drawable_get_screen (window); + GdkDisplay *display = gdk_screen_get_display (screen); + Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); + char *wm_selection_name; + Atom wm_selection; + gboolean have_wm; + + wm_selection_name = g_strdup_printf ("WM_S%d", gdk_screen_get_number (screen)); + wm_selection = gdk_x11_get_xatom_by_name_for_display (display, wm_selection_name); + g_free(wm_selection_name); + + XGrabServer (xdisplay); + + have_wm = XGetSelectionOwner (xdisplay, wm_selection) != None; + + if (have_wm) + { + /* code borrowed from GDK + */ + XClientMessageEvent xclient; + + memset (&xclient, 0, sizeof (xclient)); + xclient.type = ClientMessage; + xclient.serial = 0; + xclient.send_event = True; + xclient.window = GDK_WINDOW_XWINDOW (window); + xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"); + xclient.format = 32; + + xclient.data.l[0] = desktop; + xclient.data.l[1] = 0; + xclient.data.l[2] = 0; + xclient.data.l[3] = 0; + xclient.data.l[4] = 0; + + XSendEvent (xdisplay, + GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)), + False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *)&xclient); + } + else + { + gulong long_desktop = desktop; + + XChangeProperty (xdisplay, + GDK_DRAWABLE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_DESKTOP"), + XA_CARDINAL, 32, PropModeReplace, + (guchar *)&long_desktop, 1); + } + + XUngrabServer (xdisplay); + XFlush (xdisplay); +} + +/* Asks the window manager to turn off the "demands attention" state on the window. + * + * This only works for windows that are currently window managed; if the window + * is unmapped (in the withdrawn state) it would be necessary to change _NET_WM_STATE + * directly. + */ +void +terminal_util_x11_clear_demands_attention (GdkWindow *window) +{ + GdkScreen *screen = gdk_drawable_get_screen (window); + GdkDisplay *display = gdk_screen_get_display (screen); + XClientMessageEvent xclient; + + memset (&xclient, 0, sizeof (xclient)); + xclient.type = ClientMessage; + xclient.serial = 0; + xclient.send_event = True; + xclient.window = GDK_WINDOW_XWINDOW (window); + xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"); + xclient.format = 32; + + xclient.data.l[0] = 0; /* _NET_WM_STATE_REMOVE */ + xclient.data.l[1] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_DEMANDS_ATTENTION"); + xclient.data.l[2] = 0; + xclient.data.l[3] = 0; + xclient.data.l[4] = 0; + + XSendEvent (GDK_DISPLAY_XDISPLAY (display), + GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)), + False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *)&xclient); +} + +/* Check if a GdkWindow is minimized. This is a workaround for a + * GDK bug/misfeature. gdk_window_get_state (window) has the + * GDK_WINDOW_STATE_ICONIFIED bit for all unmapped windows, + * even windows on another desktop. + * + * http://bugzilla.mate.org/show_bug.cgi?id=586664 + * + * Code to read _NET_WM_STATE adapted from GDK + */ +gboolean +terminal_util_x11_window_is_minimized (GdkWindow *window) +{ + GdkDisplay *display = gdk_drawable_get_display (window); + + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + guchar *data; + Atom *atoms = NULL; + gulong i; + + gboolean minimized = FALSE; + + type = None; + gdk_error_trap_push (); + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"), + 0, G_MAXLONG, False, XA_ATOM, &type, &format, &nitems, + &bytes_after, &data); + gdk_error_trap_pop (); + + if (type != None) + { + Atom hidden_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_HIDDEN"); + + atoms = (Atom *)data; + + for (i = 0; i < nitems; i++) + { + if (atoms[i] == hidden_atom) + minimized = TRUE; + + ++i; + } + + XFree (atoms); + } + + return minimized; +} + +#endif /* GDK_WINDOWING_X11 */ diff --git a/src/terminal-util.h b/src/terminal-util.h new file mode 100644 index 0000000..524d41c --- /dev/null +++ b/src/terminal-util.h @@ -0,0 +1,112 @@ +/* + * Copyright © 2001 Havoc Pennington + * Copyright © 2008 Christian Persch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TERMINAL_UTIL_H +#define TERMINAL_UTIL_H + +#include <gtk/gtk.h> +#include <mateconf/mateconf-client.h> + +#include "terminal-screen.h" + +G_BEGIN_DECLS + +#define CONF_PROXY_PREFIX "/system/proxy" +#define CONF_HTTP_PROXY_PREFIX "/system/http_proxy" + +void terminal_util_set_unique_role (GtkWindow *window, const char *prefix); + +void terminal_util_show_error_dialog (GtkWindow *transient_parent, + GtkWidget **weap_ptr, + GError *error, + const char *message_format, ...) G_GNUC_PRINTF(4, 5); + +void terminal_util_show_help (const char *topic, GtkWindow *transient_parent); + +void terminal_util_set_labelled_by (GtkWidget *widget, + GtkLabel *label); +void terminal_util_set_atk_name_description (GtkWidget *widget, + const char *name, + const char *desc); + +void terminal_util_open_url (GtkWidget *parent, + const char *orig_url, + TerminalURLFlavour flavor, + guint32 user_time); + +char *terminal_util_resolve_relative_path (const char *path, + const char *relative_path); + +void terminal_util_transform_uris_to_quoted_fuse_paths (char **uris); + +char *terminal_util_concat_uris (char **uris, + gsize *length); + +char *terminal_util_get_licence_text (void); + +gboolean terminal_util_load_builder_file (const char *filename, + const char *object_name, + ...); + +gboolean terminal_util_dialog_response_on_delete (GtkWindow *widget); + +void terminal_util_key_file_set_string_escape (GKeyFile *key_file, + const char *group, + const char *key, + const char *string); +char *terminal_util_key_file_get_string_unescape (GKeyFile *key_file, + const char *group, + const char *key, + GError **error); + +void terminal_util_key_file_set_argv (GKeyFile *key_file, + const char *group, + const char *key, + int argc, + char **argv); +char **terminal_util_key_file_get_argv (GKeyFile *key_file, + const char *group, + const char *key, + int *argc, + GError **error); + +void terminal_util_add_proxy_env (GHashTable *env_table); + +typedef enum { + FLAG_INVERT_BOOL = 1 << 0, +} PropertyChangeFlags; + +void terminal_util_bind_object_property_to_widget (GObject *object, + const char *object_prop, + GtkWidget *widget, + PropertyChangeFlags flags); + +gboolean terminal_util_x11_get_net_wm_desktop (GdkWindow *window, + guint32 *desktop); +void terminal_util_x11_set_net_wm_desktop (GdkWindow *window, + guint32 desktop); + +void terminal_util_x11_clear_demands_attention (GdkWindow *window); + +gboolean terminal_util_x11_window_is_minimized (GdkWindow *window); + +G_END_DECLS + +#endif /* TERMINAL_UTIL_H */ diff --git a/src/terminal-version.h.in b/src/terminal-version.h.in new file mode 100644 index 0000000..5bc4f2d --- /dev/null +++ b/src/terminal-version.h.in @@ -0,0 +1,35 @@ +/* + * Copyright © 2009 Christian Persch + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library 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 Lesser General Public License along + * with this library; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA + */ + +#if !defined (__TERMINAL_TERMINAL_H_INSIDE__) && !defined (TERMINAL_COMPILATION) +#error "Only <terminal/terminal.h> can be included directly." +#endif + +#ifndef TERMINAL_VERSION_H +#define TERMINAL_VERSION_H + +#define TERMINAL_MAJOR_VERSION (@TERMINAL_MAJOR_VERSION@) +#define TERMINAL_MINOR_VERSION (@TERMINAL_MINOR_VERSION@) +#define TERMINAL_MICRO_VERSION (@TERMINAL_MICRO_VERSION@) + +#define TERMINAL_CHECK_VERSION(major,minor,micro) \ + (TERMINAL_MAJOR_VERSION > (major) || \ + (TERMINAL_MAJOR_VERSION == (major) && TERMINAL_MINOR_VERSION > (minor)) || \ + (TERMINAL_MAJOR_VERSION == (major) && TERMINAL_MINOR_VERSION == (minor) && TERMINAL_MICRO_VERSION >= (micro))) + +#endif /* !TERMINAL_VERSION_H */ diff --git a/src/terminal-window.c b/src/terminal-window.c new file mode 100644 index 0000000..234d209 --- /dev/null +++ b/src/terminal-window.c @@ -0,0 +1,4026 @@ +/* + * Copyright © 2001 Havoc Pennington + * Copyright © 2002 Red Hat, Inc. + * Copyright © 2007, 2008, 2009 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#include <gtk/gtk.h> +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif +#include <gdk/gdkkeysyms.h> + +#include "terminal-accels.h" +#include "terminal-app.h" +#include "terminal-debug.h" +#include "terminal-encoding.h" +#include "terminal-intl.h" +#include "terminal-screen-container.h" +#include "terminal-search-dialog.h" +#include "terminal-tab-label.h" +#include "terminal-tabs-menu.h" +#include "terminal-util.h" +#include "terminal-window.h" + +#ifdef ENABLE_SKEY +#include "skey-popup.h" +#endif + +struct _TerminalWindowPrivate +{ + GtkActionGroup *action_group; + GtkUIManager *ui_manager; + guint ui_id; + + GtkActionGroup *profiles_action_group; + guint profiles_ui_id; + + GtkActionGroup *encodings_action_group; + guint encodings_ui_id; + + TerminalTabsMenu *tabs_menu; + + TerminalScreenPopupInfo *popup_info; + guint remove_popup_info_idle; + + GtkActionGroup *new_terminal_action_group; + guint new_terminal_ui_id; + + GtkWidget *menubar; + GtkWidget *notebook; + TerminalScreen *active_screen; + int old_char_width; + int old_char_height; + void *old_geometry_widget; /* only used for pointer value as it may be freed */ + + GtkWidget *confirm_close_dialog; + GtkWidget *search_find_dialog; + + guint menubar_visible : 1; + guint use_default_menubar_visibility : 1; + + /* Compositing manager integration */ + guint have_argb_visual : 1; + + /* Used to clear stray "demands attention" flashing on our window when we + * unmap and map it to switch to an ARGB visual. + */ + guint clear_demands_attention : 1; + + guint disposed : 1; + guint present_on_insert : 1; + + /* Workaround until gtk+ bug #535557 is fixed */ + guint icon_title_set : 1; +}; + +#define PROFILE_DATA_KEY "GT::Profile" + +#define FILE_NEW_TERMINAL_TAB_UI_PATH "/menubar/File/FileNewTabProfiles" +#define FILE_NEW_TERMINAL_WINDOW_UI_PATH "/menubar/File/FileNewWindowProfiles" +#define SET_ENCODING_UI_PATH "/menubar/Terminal/TerminalSetEncoding/EncodingsPH" +#define SET_ENCODING_ACTION_NAME_PREFIX "TerminalSetEncoding" + +#define PROFILES_UI_PATH "/menubar/Terminal/TerminalProfiles" +#define PROFILES_POPUP_UI_PATH "/Popup/PopupTerminalProfiles/ProfilesPH" + +#define SIZE_TO_UI_PATH "/menubar/Terminal/TerminalSizeToPH" +#define SIZE_TO_ACTION_NAME_PREFIX "TerminalSizeTo" + +#define STOCK_NEW_WINDOW "window-new" +#define STOCK_NEW_TAB "tab-new" + +#define ENCODING_DATA_KEY "encoding" + +#if 1 +/* + * We don't want to enable content saving until vte supports it async. + * So we disable this code for stable versions. + */ +#include "terminal-version.h" + +#if (TERMINAL_MINOR_VERSION & 1) != 0 +#define ENABLE_SAVE +#else +#undef ENABLE_SAVE +#endif +#endif + +static void terminal_window_dispose (GObject *object); +static void terminal_window_finalize (GObject *object); +static gboolean terminal_window_state_event (GtkWidget *widget, + GdkEventWindowState *event); + +static gboolean terminal_window_delete_event (GtkWidget *widget, + GdkEvent *event, + gpointer data); + +static gboolean notebook_button_press_cb (GtkWidget *notebook, + GdkEventButton *event, + TerminalWindow *window); +static gboolean notebook_popup_menu_cb (GtkWidget *notebook, + TerminalWindow *window); +static void notebook_page_selected_callback (GtkWidget *notebook, +#if GTK_CHECK_VERSION (2, 90, 6) + GtkWidget *page, +#else + gpointer page, +#endif + guint page_num, + TerminalWindow *window); +static void notebook_page_added_callback (GtkWidget *notebook, + GtkWidget *container, + guint page_num, + TerminalWindow *window); +static void notebook_page_removed_callback (GtkWidget *notebook, + GtkWidget *container, + guint page_num, + TerminalWindow *window); + +/* Menu action callbacks */ +static void file_new_window_callback (GtkAction *action, + TerminalWindow *window); +static void file_new_tab_callback (GtkAction *action, + TerminalWindow *window); +static void file_new_profile_callback (GtkAction *action, + TerminalWindow *window); +static void file_close_window_callback (GtkAction *action, + TerminalWindow *window); +static void file_save_contents_callback (GtkAction *action, + TerminalWindow *window); +static void file_close_tab_callback (GtkAction *action, + TerminalWindow *window); +static void edit_copy_callback (GtkAction *action, + TerminalWindow *window); +static void edit_paste_callback (GtkAction *action, + TerminalWindow *window); +static void edit_select_all_callback (GtkAction *action, + TerminalWindow *window); +static void edit_keybindings_callback (GtkAction *action, + TerminalWindow *window); +static void edit_profiles_callback (GtkAction *action, + TerminalWindow *window); +static void edit_current_profile_callback (GtkAction *action, + TerminalWindow *window); +static void view_menubar_toggled_callback (GtkToggleAction *action, + TerminalWindow *window); +static void view_fullscreen_toggled_callback (GtkToggleAction *action, + TerminalWindow *window); +static void view_zoom_in_callback (GtkAction *action, + TerminalWindow *window); +static void view_zoom_out_callback (GtkAction *action, + TerminalWindow *window); +static void view_zoom_normal_callback (GtkAction *action, + TerminalWindow *window); +static void search_find_callback (GtkAction *action, + TerminalWindow *window); +static void search_find_next_callback (GtkAction *action, + TerminalWindow *window); +static void search_find_prev_callback (GtkAction *action, + TerminalWindow *window); +static void search_clear_highlight_callback (GtkAction *action, + TerminalWindow *window); +static void terminal_set_title_callback (GtkAction *action, + TerminalWindow *window); +static void terminal_add_encoding_callback (GtkAction *action, + TerminalWindow *window); +static void terminal_reset_callback (GtkAction *action, + TerminalWindow *window); +static void terminal_reset_clear_callback (GtkAction *action, + TerminalWindow *window); +static void tabs_next_or_previous_tab_cb (GtkAction *action, + TerminalWindow *window); +static void tabs_move_left_callback (GtkAction *action, + TerminalWindow *window); +static void tabs_move_right_callback (GtkAction *action, + TerminalWindow *window); +static void tabs_detach_tab_callback (GtkAction *action, + TerminalWindow *window); +static void help_contents_callback (GtkAction *action, + TerminalWindow *window); +static void help_about_callback (GtkAction *action, + TerminalWindow *window); + +static gboolean find_larger_zoom_factor (double current, + double *found); +static gboolean find_smaller_zoom_factor (double current, + double *found); + +static void terminal_window_show (GtkWidget *widget); + +static gboolean confirm_close_window_or_tab (TerminalWindow *window, + TerminalScreen *screen); + +static void +profile_set_callback (TerminalScreen *screen, + TerminalProfile *old_profile, + TerminalWindow *window); +static void +sync_screen_icon_title (TerminalScreen *screen, + GParamSpec *psepc, + TerminalWindow *window); + +G_DEFINE_TYPE (TerminalWindow, terminal_window, GTK_TYPE_WINDOW) + +/* Menubar mnemonics & accel settings handling */ + +static void +app_setting_notify_cb (TerminalApp *app, + GParamSpec *pspec, + GdkScreen *screen) +{ + GtkSettings *settings; + const char *prop_name; + + if (pspec) + prop_name = pspec->name; + else + prop_name = NULL; + + settings = gtk_settings_get_for_screen (screen); + + if (!prop_name || prop_name == I_(TERMINAL_APP_ENABLE_MNEMONICS)) + { + gboolean enable_mnemonics; + + g_object_get (app, TERMINAL_APP_ENABLE_MNEMONICS, &enable_mnemonics, NULL); + g_object_set (settings, "gtk-enable-mnemonics", enable_mnemonics, NULL); + } + + if (!prop_name || prop_name == I_(TERMINAL_APP_ENABLE_MENU_BAR_ACCEL)) + { + /* const */ char *saved_menubar_accel; + gboolean enable_menubar_accel; + + /* FIXME: Once gtk+ bug 507398 is fixed, use that to reset the property instead */ + /* Now this is a bad hack on so many levels. */ + saved_menubar_accel = g_object_get_data (G_OBJECT (settings), "GT::gtk-menu-bar-accel"); + if (!saved_menubar_accel) + { + g_object_get (settings, "gtk-menu-bar-accel", &saved_menubar_accel, NULL); + g_object_set_data_full (G_OBJECT (settings), "GT::gtk-menu-bar-accel", + saved_menubar_accel, (GDestroyNotify) g_free); + } + + g_object_get (app, TERMINAL_APP_ENABLE_MENU_BAR_ACCEL, &enable_menubar_accel, NULL); + if (enable_menubar_accel) + g_object_set (settings, "gtk-menu-bar-accel", saved_menubar_accel, NULL); + else + g_object_set (settings, "gtk-menu-bar-accel", NULL, NULL); + } +} + +static void +app_setting_notify_destroy_cb (GdkScreen *screen) +{ + g_signal_handlers_disconnect_by_func (terminal_app_get (), + G_CALLBACK (app_setting_notify_cb), + screen); +} + +/* utility functions */ + +static char * +escape_underscores (const char *name) +{ + GString *escaped_name; + + g_assert (name != NULL); + + /* Who'd use more that 4 underscores in a profile name... */ + escaped_name = g_string_sized_new (strlen (name) + 4 + 1); + + while (*name) + { + if (*name == '_') + g_string_append (escaped_name, "__"); + else + g_string_append_c (escaped_name, *name); + name++; + } + + return g_string_free (escaped_name, FALSE); +} + +static int +find_tab_num_at_pos (GtkNotebook *notebook, + int screen_x, + int screen_y) +{ + GtkPositionType tab_pos; + int page_num = 0; + GtkNotebook *nb = GTK_NOTEBOOK (notebook); + GtkWidget *page; + GtkAllocation tab_allocation; + + tab_pos = gtk_notebook_get_tab_pos (GTK_NOTEBOOK (notebook)); + + while ((page = gtk_notebook_get_nth_page (nb, page_num))) + { + GtkWidget *tab; + int max_x, max_y, x_root, y_root; + + tab = gtk_notebook_get_tab_label (nb, page); + g_return_val_if_fail (tab != NULL, -1); + + if (!gtk_widget_get_mapped (GTK_WIDGET (tab))) + { + page_num++; + continue; + } + + gdk_window_get_origin (gtk_widget_get_window (tab), &x_root, &y_root); + + gtk_widget_get_allocation (tab, &tab_allocation); + max_x = x_root + tab_allocation.x + tab_allocation.width; + max_y = y_root + tab_allocation.y + tab_allocation.height; + + if ((tab_pos == GTK_POS_TOP || tab_pos == GTK_POS_BOTTOM) && screen_x <= max_x) + return page_num; + + if ((tab_pos == GTK_POS_LEFT || tab_pos == GTK_POS_RIGHT) && screen_y <= max_y) + return page_num; + + page_num++; + } + + return -1; +} + +static void +position_menu_under_widget (GtkMenu *menu, + int *x, + int *y, + gboolean *push_in, + gpointer user_data) +{ + /* Adapted from gtktoolbar.c */ + GtkWidget *widget = GTK_WIDGET (user_data); + GdkWindow *widget_window; + GtkWidget *container; + GtkRequisition req; + GtkRequisition menu_req; + GdkRectangle monitor; + int monitor_num; + GdkScreen *screen; + GtkAllocation widget_allocation; + + widget_window = gtk_widget_get_window (widget); + gtk_widget_get_allocation (widget, &widget_allocation); + container = gtk_widget_get_ancestor (widget, GTK_TYPE_CONTAINER); + + gtk_widget_size_request (widget, &req); + gtk_widget_size_request (GTK_WIDGET (menu), &menu_req); + + screen = gtk_widget_get_screen (GTK_WIDGET (menu)); + monitor_num = gdk_screen_get_monitor_at_window (screen, widget_window); + if (monitor_num < 0) + monitor_num = 0; + gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); + + gdk_window_get_origin (widget_window, x, y); + if (!gtk_widget_get_has_window (widget)) + { + *x += widget_allocation.x; + *y += widget_allocation.y; + } + if (gtk_widget_get_direction (container) == GTK_TEXT_DIR_LTR) + *x += widget_allocation.width - req.width; + else + *x += req.width - menu_req.width; + + if ((*y + widget_allocation.height + menu_req.height) <= monitor.y + monitor.height) + *y += widget_allocation.height; + else if ((*y - menu_req.height) >= monitor.y) + *y -= menu_req.height; + else if (monitor.y + monitor.height - (*y + widget_allocation.height) > *y) + *y += widget_allocation.height; + else + *y -= menu_req.height; + + *push_in = FALSE; +} + +static void +terminal_set_profile_toggled_callback (GtkToggleAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalProfile *profile; + + if (!gtk_toggle_action_get_active (action)) + return; + + if (priv->active_screen == NULL) + return; + + profile = g_object_get_data (G_OBJECT (action), PROFILE_DATA_KEY); + g_assert (profile); + + if (_terminal_profile_get_forgotten (profile)) + return; + + g_signal_handlers_block_by_func (priv->active_screen, G_CALLBACK (profile_set_callback), window); + terminal_screen_set_profile (priv->active_screen, profile); + g_signal_handlers_unblock_by_func (priv->active_screen, G_CALLBACK (profile_set_callback), window); +} + +static void +profile_visible_name_notify_cb (TerminalProfile *profile, + GParamSpec *pspec, + GtkAction *action) +{ + const char *visible_name; + char *dot, *display_name; + guint num; + + visible_name = terminal_profile_get_property_string (profile, TERMINAL_PROFILE_VISIBLE_NAME); + display_name = escape_underscores (visible_name); + + dot = strchr (gtk_action_get_name (action), '.'); + if (dot != NULL) + { + char *free_me; + + num = g_ascii_strtoll (dot + 1, NULL, 10); + + free_me = display_name; + if (num < 10) + /* Translators: This is the label of a menu item to choose a profile. + * _%d is used as the accelerator (with d between 1 and 9), and + * the %s is the name of the terminal profile. + */ + display_name = g_strdup_printf (_("_%d. %s"), num, display_name); + else if (num < 36) + /* Translators: This is the label of a menu item to choose a profile. + * _%c is used as the accelerator (it will be a character between A and Z), + * and the %s is the name of the terminal profile. + */ + display_name = g_strdup_printf (_("_%c. %s"), ('A' + num - 10), display_name); + else + free_me = NULL; + + g_free (free_me); + } + + g_object_set (action, "label", display_name, NULL); + g_free (display_name); +} + +static void +disconnect_profiles_from_actions_in_group (GtkActionGroup *action_group) +{ + GList *actions, *l; + + actions = gtk_action_group_list_actions (action_group); + for (l = actions; l != NULL; l = l->next) + { + GObject *action = G_OBJECT (l->data); + TerminalProfile *profile; + + profile = g_object_get_data (action, PROFILE_DATA_KEY); + if (!profile) + continue; + + g_signal_handlers_disconnect_by_func (profile, G_CALLBACK (profile_visible_name_notify_cb), action); + } + g_list_free (actions); +} + +static void +terminal_window_update_set_profile_menu_active_profile (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalProfile *new_active_profile; + GList *actions, *l; + + if (!priv->profiles_action_group) + return; + + if (!priv->active_screen) + return; + + new_active_profile = terminal_screen_get_profile (priv->active_screen); + + actions = gtk_action_group_list_actions (priv->profiles_action_group); + for (l = actions; l != NULL; l = l->next) + { + GObject *action = G_OBJECT (l->data); + TerminalProfile *profile; + + profile = g_object_get_data (action, PROFILE_DATA_KEY); + if (profile != new_active_profile) + continue; + + g_signal_handlers_block_by_func (action, G_CALLBACK (terminal_set_profile_toggled_callback), window); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + g_signal_handlers_unblock_by_func (action, G_CALLBACK (terminal_set_profile_toggled_callback), window); + + break; + } + g_list_free (actions); +} + +static void +terminal_window_update_set_profile_menu (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalProfile *active_profile; + GtkActionGroup *action_group; + GtkAction *action; + GList *profiles, *p; + GSList *group; + guint n; + gboolean single_profile; + + /* Remove the old UI */ + if (priv->profiles_ui_id != 0) + { + gtk_ui_manager_remove_ui (priv->ui_manager, priv->profiles_ui_id); + priv->profiles_ui_id = 0; + } + + if (priv->profiles_action_group != NULL) + { + disconnect_profiles_from_actions_in_group (priv->profiles_action_group); + gtk_ui_manager_remove_action_group (priv->ui_manager, + priv->profiles_action_group); + priv->profiles_action_group = NULL; + } + + profiles = terminal_app_get_profile_list (terminal_app_get ()); + + action = gtk_action_group_get_action (priv->action_group, "TerminalProfiles"); + single_profile = !profiles || profiles->next == NULL; /* list length <= 1 */ + gtk_action_set_sensitive (action, !single_profile); + if (profiles == NULL) + return; + + if (priv->active_screen) + active_profile = terminal_screen_get_profile (priv->active_screen); + else + active_profile = NULL; + + action_group = priv->profiles_action_group = gtk_action_group_new ("Profiles"); + gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, -1); + g_object_unref (action_group); + + priv->profiles_ui_id = gtk_ui_manager_new_merge_id (priv->ui_manager); + + group = NULL; + n = 0; + for (p = profiles; p != NULL; p = p->next) + { + TerminalProfile *profile = (TerminalProfile *) p->data; + GtkRadioAction *profile_action; + char name[32]; + + g_snprintf (name, sizeof (name), "TerminalSetProfile%u", n++); + + profile_action = gtk_radio_action_new (name, + NULL, + NULL, + NULL, + n); + + gtk_radio_action_set_group (profile_action, group); + group = gtk_radio_action_get_group (profile_action); + + if (profile == active_profile) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (profile_action), TRUE); + + g_object_set_data_full (G_OBJECT (profile_action), + PROFILE_DATA_KEY, + g_object_ref (profile), + (GDestroyNotify) g_object_unref); + profile_visible_name_notify_cb (profile, NULL, GTK_ACTION (profile_action)); + g_signal_connect (profile, "notify::" TERMINAL_PROFILE_VISIBLE_NAME, + G_CALLBACK (profile_visible_name_notify_cb), profile_action); + g_signal_connect (profile_action, "toggled", + G_CALLBACK (terminal_set_profile_toggled_callback), window); + + gtk_action_group_add_action (action_group, GTK_ACTION (profile_action)); + g_object_unref (profile_action); + + gtk_ui_manager_add_ui (priv->ui_manager, priv->profiles_ui_id, + PROFILES_UI_PATH, + name, name, + GTK_UI_MANAGER_MENUITEM, FALSE); + gtk_ui_manager_add_ui (priv->ui_manager, priv->profiles_ui_id, + PROFILES_POPUP_UI_PATH, + name, name, + GTK_UI_MANAGER_MENUITEM, FALSE); + } + + g_list_free (profiles); +} + +static void +terminal_window_create_new_terminal_action (TerminalWindow *window, + TerminalProfile *profile, + const char *name, + guint num, + GCallback callback) +{ + TerminalWindowPrivate *priv = window->priv; + GtkAction *action; + + action = gtk_action_new (name, NULL, NULL, NULL); + + g_object_set_data_full (G_OBJECT (action), + PROFILE_DATA_KEY, + g_object_ref (profile), + (GDestroyNotify) g_object_unref); + profile_visible_name_notify_cb (profile, NULL, action); + g_signal_connect (profile, "notify::" TERMINAL_PROFILE_VISIBLE_NAME, + G_CALLBACK (profile_visible_name_notify_cb), action); + g_signal_connect (action, "activate", callback, window); + + gtk_action_group_add_action (priv->new_terminal_action_group, action); + g_object_unref (action); +} + +static void +terminal_window_update_new_terminal_menus (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkActionGroup *action_group; + GtkAction *action; + GList *profiles, *p; + guint n; + gboolean have_single_profile; + + /* Remove the old UI */ + if (priv->new_terminal_ui_id != 0) + { + gtk_ui_manager_remove_ui (priv->ui_manager, priv->new_terminal_ui_id); + priv->new_terminal_ui_id = 0; + } + + if (priv->new_terminal_action_group != NULL) + { + disconnect_profiles_from_actions_in_group (priv->new_terminal_action_group); + gtk_ui_manager_remove_action_group (priv->ui_manager, + priv->new_terminal_action_group); + priv->new_terminal_action_group = NULL; + } + + profiles = terminal_app_get_profile_list (terminal_app_get ()); + have_single_profile = !profiles || !profiles->next; + + action = gtk_action_group_get_action (priv->action_group, "FileNewTab"); + gtk_action_set_visible (action, have_single_profile); + action = gtk_action_group_get_action (priv->action_group, "FileNewWindow"); + gtk_action_set_visible (action, have_single_profile); + + if (have_single_profile) + { + g_list_free (profiles); + return; + } + + /* Now build the submenus */ + + action_group = priv->new_terminal_action_group = gtk_action_group_new ("NewTerminal"); + gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, -1); + g_object_unref (action_group); + + priv->new_terminal_ui_id = gtk_ui_manager_new_merge_id (priv->ui_manager); + + n = 0; + for (p = profiles; p != NULL; p = p->next) + { + TerminalProfile *profile = (TerminalProfile *) p->data; + char name[32]; + + g_snprintf (name, sizeof (name), "FileNewTab.%u", n); + terminal_window_create_new_terminal_action (window, + profile, + name, + n, + G_CALLBACK (file_new_tab_callback)); + + gtk_ui_manager_add_ui (priv->ui_manager, priv->new_terminal_ui_id, + FILE_NEW_TERMINAL_TAB_UI_PATH, + name, name, + GTK_UI_MANAGER_MENUITEM, FALSE); + + g_snprintf (name, sizeof (name), "FileNewWindow.%u", n); + terminal_window_create_new_terminal_action (window, + profile, + name, + n, + G_CALLBACK (file_new_window_callback)); + + gtk_ui_manager_add_ui (priv->ui_manager, priv->new_terminal_ui_id, + FILE_NEW_TERMINAL_WINDOW_UI_PATH, + name, name, + GTK_UI_MANAGER_MENUITEM, FALSE); + + ++n; + } + + g_list_free (profiles); +} + +static void +terminal_set_encoding_callback (GtkToggleAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalEncoding *encoding; + + if (!gtk_toggle_action_get_active (action)) + return; + + if (priv->active_screen == NULL) + return; + + encoding = g_object_get_data (G_OBJECT (action), ENCODING_DATA_KEY); + g_assert (encoding); + + vte_terminal_set_encoding (VTE_TERMINAL (priv->active_screen), + terminal_encoding_get_charset (encoding)); +} + +static void +terminal_window_update_encoding_menu (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalApp *app; + GtkActionGroup *action_group; + GSList *group; + guint n; + GSList *encodings, *l; + const char *charset; + TerminalEncoding *active_encoding; + + /* Remove the old UI */ + if (priv->encodings_ui_id != 0) + { + gtk_ui_manager_remove_ui (priv->ui_manager, priv->encodings_ui_id); + priv->encodings_ui_id = 0; + } + + if (priv->encodings_action_group != NULL) + { + gtk_ui_manager_remove_action_group (priv->ui_manager, + priv->encodings_action_group); + priv->encodings_action_group = NULL; + } + + action_group = priv->encodings_action_group = gtk_action_group_new ("Encodings"); + gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, -1); + g_object_unref (action_group); + + priv->encodings_ui_id = gtk_ui_manager_new_merge_id (priv->ui_manager); + + if (priv->active_screen) + charset = vte_terminal_get_encoding (VTE_TERMINAL (priv->active_screen)); + else + charset = "current"; + + app = terminal_app_get (); + active_encoding = terminal_app_ensure_encoding (app, charset); + + encodings = terminal_app_get_active_encodings (app); + + if (g_slist_find (encodings, active_encoding) == NULL) + encodings = g_slist_append (encodings, terminal_encoding_ref (active_encoding)); + + group = NULL; + n = 0; + for (l = encodings; l != NULL; l = l->next) + { + TerminalEncoding *e = (TerminalEncoding *) l->data; + GtkRadioAction *encoding_action; + char name[128]; + char *display_name; + + g_snprintf (name, sizeof (name), SET_ENCODING_ACTION_NAME_PREFIX "%s", terminal_encoding_get_id (e)); + display_name = g_strdup_printf ("%s (%s)", e->name, terminal_encoding_get_charset (e)); + + encoding_action = gtk_radio_action_new (name, + display_name, + NULL, + NULL, + n); + g_free (display_name); + + gtk_radio_action_set_group (encoding_action, group); + group = gtk_radio_action_get_group (encoding_action); + + if (charset && strcmp (terminal_encoding_get_id (e), charset) == 0) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (encoding_action), TRUE); + + g_signal_connect (encoding_action, "toggled", + G_CALLBACK (terminal_set_encoding_callback), window); + + g_object_set_data_full (G_OBJECT (encoding_action), ENCODING_DATA_KEY, + terminal_encoding_ref (e), + (GDestroyNotify) terminal_encoding_unref); + + gtk_action_group_add_action (action_group, GTK_ACTION (encoding_action)); + g_object_unref (encoding_action); + + gtk_ui_manager_add_ui (priv->ui_manager, priv->encodings_ui_id, + SET_ENCODING_UI_PATH, + name, name, + GTK_UI_MANAGER_MENUITEM, FALSE); + } + + g_slist_foreach (encodings, (GFunc) terminal_encoding_unref, NULL); + g_slist_free (encodings); +} + +static void +terminal_window_update_encoding_menu_active_encoding (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkAction *action; + char name[128]; + + if (!priv->active_screen) + return; + if (!priv->encodings_action_group) + return; + + g_snprintf (name, sizeof (name), SET_ENCODING_ACTION_NAME_PREFIX "%s", + vte_terminal_get_encoding (VTE_TERMINAL (priv->active_screen))); + action = gtk_action_group_get_action (priv->encodings_action_group, name); + if (!action) + return; + + g_signal_handlers_block_by_func (action, G_CALLBACK (terminal_set_encoding_callback), window); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + g_signal_handlers_unblock_by_func (action, G_CALLBACK (terminal_set_encoding_callback), window); +} + +static void +terminal_size_to_cb (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + const char *name; + char *end = NULL; + guint width, height; + + if (priv->active_screen == NULL) + return; + + name = gtk_action_get_name (action) + strlen (SIZE_TO_ACTION_NAME_PREFIX); + width = g_ascii_strtoull (name, &end, 10); + g_assert (end && *end == 'x'); + height = g_ascii_strtoull (end + 1, &end, 10); + g_assert (end && *end == '\0'); + + vte_terminal_set_size (VTE_TERMINAL (priv->active_screen), width, height); + + terminal_window_set_size_force_grid (window, priv->active_screen, TRUE, -1, -1); +} + +static void +terminal_window_update_size_to_menu (TerminalWindow *window) +{ + static const struct { + guint grid_width; + guint grid_height; + } predefined_sizes[] = { + { 80, 24 }, + { 80, 43 }, + { 132, 24 }, + { 132, 43 } + }; + TerminalWindowPrivate *priv = window->priv; + guint i; + + /* We only install this once, so there's no need for a separate action group + * and any cleanup + build-new-one action here. + */ + + for (i = 0; i < G_N_ELEMENTS (predefined_sizes); ++i) + { + guint grid_width = predefined_sizes[i].grid_width; + guint grid_height = predefined_sizes[i].grid_height; + GtkAction *action; + char name[40]; + char *display_name; + + g_snprintf (name, sizeof (name), SIZE_TO_ACTION_NAME_PREFIX "%ux%u", + grid_width, grid_height); + + /* If there are ever more than 9 of these, extend this to use A..Z as mnemonics, + * like we do for the profiles menu. + */ + display_name = g_strdup_printf ("_%u. %ux%u", i + 1, grid_width, grid_height); + + action = gtk_action_new (name, display_name, NULL, NULL); + g_free (display_name); + + g_signal_connect (action, "activate", + G_CALLBACK (terminal_size_to_cb), window); + + gtk_action_group_add_action (priv->action_group, action); + g_object_unref (action); + + gtk_ui_manager_add_ui (priv->ui_manager, priv->ui_id, + SIZE_TO_UI_PATH, + name, name, + GTK_UI_MANAGER_MENUITEM, FALSE); + } +} + +/* Actions stuff */ + +static void +terminal_window_update_copy_sensitivity (TerminalScreen *screen, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkAction *action; + gboolean can_copy; + + if (screen != priv->active_screen) + return; + + can_copy = vte_terminal_get_has_selection (VTE_TERMINAL (screen)); + + action = gtk_action_group_get_action (priv->action_group, "EditCopy"); + gtk_action_set_sensitive (action, can_copy); +} + +static void +terminal_window_update_zoom_sensitivity (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalScreen *screen; + GtkAction *action; + double current, zoom; + + screen = priv->active_screen; + if (screen == NULL) + return; + + current = terminal_screen_get_font_scale (screen); + + action = gtk_action_group_get_action (priv->action_group, "ViewZoomOut"); + gtk_action_set_sensitive (action, find_smaller_zoom_factor (current, &zoom)); + action = gtk_action_group_get_action (priv->action_group, "ViewZoomIn"); + gtk_action_set_sensitive (action, find_larger_zoom_factor (current, &zoom)); +} + +static void +terminal_window_update_search_sensitivity (TerminalScreen *screen, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkAction *action; + gboolean can_search; + + if (screen != priv->active_screen) + return; + + can_search = vte_terminal_search_get_gregex (VTE_TERMINAL (screen)) != NULL; + + action = gtk_action_group_get_action (priv->action_group, "SearchFindNext"); + gtk_action_set_sensitive (action, can_search); + action = gtk_action_group_get_action (priv->action_group, "SearchFindPrevious"); + gtk_action_set_sensitive (action, can_search); + action = gtk_action_group_get_action (priv->action_group, "SearchClearHighlight"); + gtk_action_set_sensitive (action, can_search); +} + +static void +update_edit_menu_cb (GtkClipboard *clipboard, + GdkAtom *targets, + int n_targets, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkAction *action; + gboolean can_paste, can_paste_uris; + + can_paste = targets != NULL && gtk_targets_include_text (targets, n_targets); + can_paste_uris = targets != NULL && gtk_targets_include_uri (targets, n_targets); + + action = gtk_action_group_get_action (priv->action_group, "EditPaste"); + gtk_action_set_sensitive (action, can_paste); + action = gtk_action_group_get_action (priv->action_group, "EditPasteURIPaths"); + gtk_action_set_visible (action, can_paste_uris); + gtk_action_set_sensitive (action, can_paste_uris); + + /* Ref was added in gtk_clipboard_request_targets below */ + g_object_unref (window); +} + +static void +edit_menu_activate_callback (GtkMenuItem *menuitem, + gpointer user_data) +{ + TerminalWindow *window = (TerminalWindow *) user_data; + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD); + gtk_clipboard_request_targets (clipboard, + (GtkClipboardTargetsReceivedFunc) update_edit_menu_cb, + g_object_ref (window)); +} + +static void +screen_resize_window_cb (TerminalScreen *screen, + guint width, + guint height, + TerminalWindow* window) +{ + TerminalWindowPrivate *priv = window->priv; + VteTerminal *terminal = VTE_TERMINAL (screen); + GtkWidget *widget = GTK_WIDGET (screen); + guint grid_width, grid_height; + int char_width, char_height; + GtkBorder *inner_border = NULL; + GtkAllocation widget_allocation; + + gtk_widget_get_allocation (widget, &widget_allocation); + /* Don't do anything if we're maximised or fullscreened */ + // FIXME: realized && ... instead? + if (!gtk_widget_get_realized (widget) || + (gdk_window_get_state (gtk_widget_get_window (widget)) & (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) != 0) + return; + + /* NOTE: width and height already include the VteTerminal's padding! */ + + /* Short-circuit */ + if (((int) width) == widget_allocation.width && + ((int) height) == widget_allocation.height) + return; + + /* The resize-window signal sucks. Re-compute grid widths */ + + char_width = vte_terminal_get_char_width (terminal); + char_height = vte_terminal_get_char_height (terminal); + + gtk_widget_style_get (GTK_WIDGET (terminal), "inner-border", &inner_border, NULL); + grid_width = (width - (inner_border ? (inner_border->left + inner_border->right) : 0)) / char_width; + grid_height = (height - (inner_border ? (inner_border->top + inner_border->bottom) : 0)) / char_height; + gtk_border_free (inner_border); + + vte_terminal_set_size (terminal, grid_width, grid_height); + + if (screen != priv->active_screen) + return; + + terminal_window_set_size_force_grid (window, screen, TRUE, -1, -1); //grid_width, grid_height); +} + +static void +terminal_window_update_tabs_menu_sensitivity (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkNotebook *notebook = GTK_NOTEBOOK (priv->notebook); + GtkActionGroup *action_group = priv->action_group; + GtkAction *action; + int num_pages, page_num; + gboolean not_first, not_last; + + if (priv->disposed) + return; + + num_pages = gtk_notebook_get_n_pages (notebook); + page_num = gtk_notebook_get_current_page (notebook); + not_first = page_num > 0; + not_last = page_num + 1 < num_pages; + + /* Hide the tabs menu in single-tab windows */ + action = gtk_action_group_get_action (action_group, "Tabs"); + gtk_action_set_visible (action, num_pages > 1); + +#if 1 + /* NOTE: We always make next/prev actions sensitive except in + * single-tab windows, so the corresponding shortcut key escape code + * isn't sent to the terminal. See bug #453193 and bug #138609. + * This also makes tab cycling work, bug #92139. + * FIXME: Find a better way to do this. + */ + action = gtk_action_group_get_action (action_group, "TabsPrevious"); + gtk_action_set_sensitive (action, num_pages > 1); + action = gtk_action_group_get_action (action_group, "TabsNext"); + gtk_action_set_sensitive (action, num_pages > 1); +#else + /* This would be correct, but see the comment above. */ + action = gtk_action_group_get_action (action_group, "TabsPrevious"); + gtk_action_set_sensitive (action, not_first); + action = gtk_action_group_get_action (action_group, "TabsNext"); + gtk_action_set_sensitive (action, not_last); +#endif + + action = gtk_action_group_get_action (action_group, "TabsMoveLeft"); + gtk_action_set_sensitive (action, not_first); + action = gtk_action_group_get_action (action_group, "TabsMoveRight"); + gtk_action_set_sensitive (action, not_last); + action = gtk_action_group_get_action (action_group, "TabsDetach"); + gtk_action_set_sensitive (action, num_pages > 1); + action = gtk_action_group_get_action (action_group, "FileCloseTab"); + gtk_action_set_sensitive (action, num_pages > 1); +} + +gboolean +terminal_window_uses_argb_visual (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + return priv->have_argb_visual; +} + +static void +update_tab_visibility (TerminalWindow *window, + int change) +{ + TerminalWindowPrivate *priv = window->priv; + gboolean show_tabs; + guint num; + + num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)); + + show_tabs = (num + change) > 1; + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), show_tabs); +} + +static GtkNotebook * +handle_tab_droped_on_desktop (GtkNotebook *source_notebook, + GtkWidget *container, + gint x, + gint y, + gpointer data) +{ + TerminalScreen *screen; + TerminalWindow *source_window; + TerminalWindow *new_window; + TerminalWindowPrivate *new_priv; + + screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (container)); + source_window = TERMINAL_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (source_notebook))); + g_return_val_if_fail (TERMINAL_IS_WINDOW (source_window), NULL); + + new_window = terminal_app_new_window (terminal_app_get (), + gtk_widget_get_screen (GTK_WIDGET (source_window))); + new_priv = new_window->priv; + new_priv->present_on_insert = TRUE; + + update_tab_visibility (source_window, -1); + update_tab_visibility (new_window, +1); + + return GTK_NOTEBOOK (new_priv->notebook); +} + +/* Terminal screen popup menu handling */ + +static void +popup_open_url_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalScreenPopupInfo *info = priv->popup_info; + + if (info == NULL) + return; + + terminal_util_open_url (GTK_WIDGET (window), info->string, info->flavour, + gtk_get_current_event_time ()); +} + +static void +popup_copy_url_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalScreenPopupInfo *info = priv->popup_info; + GtkClipboard *clipboard; + + if (info == NULL) + return; + + if (info->string == NULL) + return; + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text (clipboard, info->string, -1); +} + +static void +popup_leave_fullscreen_callback (GtkAction *action, + TerminalWindow *window) +{ + gtk_window_unfullscreen (GTK_WINDOW (window)); +} + +static void +remove_popup_info (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (priv->remove_popup_info_idle != 0) + { + g_source_remove (priv->remove_popup_info_idle); + priv->remove_popup_info_idle = 0; + } + + if (priv->popup_info != NULL) + { + terminal_screen_popup_info_unref (priv->popup_info); + priv->popup_info = NULL; + } +} + +static gboolean +idle_remove_popup_info (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + priv->remove_popup_info_idle = 0; + remove_popup_info (window); + return FALSE; +} + +static void +unset_popup_info (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + /* Unref the event from idle since we still need it + * from the action callbacks which will run before idle. + */ + if (priv->remove_popup_info_idle == 0 && + priv->popup_info != NULL) + { + priv->remove_popup_info_idle = + g_idle_add ((GSourceFunc) idle_remove_popup_info, window); + } +} + +static void +popup_menu_deactivate_callback (GtkWidget *popup, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkWidget *im_menu_item; + + g_signal_handlers_disconnect_by_func + (popup, G_CALLBACK (popup_menu_deactivate_callback), window); + + im_menu_item = gtk_ui_manager_get_widget (priv->ui_manager, + "/Popup/PopupInputMethods"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (im_menu_item), NULL); + + unset_popup_info (window); +} + +static void +popup_clipboard_targets_received_cb (GtkClipboard *clipboard, + GdkAtom *targets, + int n_targets, + TerminalScreenPopupInfo *info) +{ + TerminalWindow *window = info->window; + TerminalWindowPrivate *priv = window->priv; + TerminalScreen *screen = info->screen; + GtkWidget *popup_menu, *im_menu, *im_menu_item; + GtkAction *action; + gboolean can_paste, can_paste_uris, show_link, show_email_link, show_call_link, show_input_method_menu; + int n_pages; + + if (!gtk_widget_get_realized (GTK_WIDGET (screen))) + { + terminal_screen_popup_info_unref (info); + return; + } + + /* Now we know that the screen is realized, we know that the window is still alive */ + remove_popup_info (window); + + priv->popup_info = info; /* adopt the ref added when requesting the clipboard */ + + n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)); + + can_paste = targets != NULL && gtk_targets_include_text (targets, n_targets); + can_paste_uris = targets != NULL && gtk_targets_include_uri (targets, n_targets); + show_link = info->string != NULL && (info->flavour == FLAVOR_AS_IS || info->flavour == FLAVOR_DEFAULT_TO_HTTP); + show_email_link = info->string != NULL && info->flavour == FLAVOR_EMAIL; + show_call_link = info->string != NULL && info->flavour == FLAVOR_VOIP_CALL; + + action = gtk_action_group_get_action (priv->action_group, "PopupSendEmail"); + gtk_action_set_visible (action, show_email_link); + action = gtk_action_group_get_action (priv->action_group, "PopupCopyEmailAddress"); + gtk_action_set_visible (action, show_email_link); + action = gtk_action_group_get_action (priv->action_group, "PopupCall"); + gtk_action_set_visible (action, show_call_link); + action = gtk_action_group_get_action (priv->action_group, "PopupCopyCallAddress"); + gtk_action_set_visible (action, show_call_link); + action = gtk_action_group_get_action (priv->action_group, "PopupOpenLink"); + gtk_action_set_visible (action, show_link); + action = gtk_action_group_get_action (priv->action_group, "PopupCopyLinkAddress"); + gtk_action_set_visible (action, show_link); + + action = gtk_action_group_get_action (priv->action_group, "PopupCloseWindow"); + gtk_action_set_visible (action, n_pages <= 1); + action = gtk_action_group_get_action (priv->action_group, "PopupCloseTab"); + gtk_action_set_visible (action, n_pages > 1); + + action = gtk_action_group_get_action (priv->action_group, "PopupCopy"); + gtk_action_set_sensitive (action, vte_terminal_get_has_selection (VTE_TERMINAL (screen))); + action = gtk_action_group_get_action (priv->action_group, "PopupPaste"); + gtk_action_set_sensitive (action, can_paste); + action = gtk_action_group_get_action (priv->action_group, "PopupPasteURIPaths"); + gtk_action_set_visible (action, can_paste_uris); + + g_object_get (gtk_widget_get_settings (GTK_WIDGET (window)), + "gtk-show-input-method-menu", &show_input_method_menu, + NULL); + + action = gtk_action_group_get_action (priv->action_group, "PopupInputMethods"); + gtk_action_set_visible (action, show_input_method_menu); + + im_menu_item = gtk_ui_manager_get_widget (priv->ui_manager, + "/Popup/PopupInputMethods"); + /* FIXME: fix this when gtk+ bug #500065 is done, use vte_terminal_im_merge_ui */ + if (show_input_method_menu) + { + im_menu = gtk_menu_new (); + vte_terminal_im_append_menuitems (VTE_TERMINAL (screen), + GTK_MENU_SHELL (im_menu)); + gtk_widget_show (im_menu); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (im_menu_item), im_menu); + } + else + { + gtk_menu_item_set_submenu (GTK_MENU_ITEM (im_menu_item), NULL); + } + + popup_menu = gtk_ui_manager_get_widget (priv->ui_manager, "/Popup"); + g_signal_connect (popup_menu, "deactivate", + G_CALLBACK (popup_menu_deactivate_callback), window); + + /* Pseudo activation of the popup menu's action */ + action = gtk_action_group_get_action (priv->action_group, "Popup"); + gtk_action_activate (action); + + if (info->button == 0) + gtk_menu_shell_select_first (GTK_MENU_SHELL (popup_menu), FALSE); + + gtk_menu_popup (GTK_MENU (popup_menu), + NULL, NULL, + NULL, NULL, + info->button, + info->timestamp); +} + +static void +screen_show_popup_menu_callback (TerminalScreen *screen, + TerminalScreenPopupInfo *info, + TerminalWindow *window) +{ + GtkClipboard *clipboard; + + g_return_if_fail (info->window == window); + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD); + gtk_clipboard_request_targets (clipboard, + (GtkClipboardTargetsReceivedFunc) popup_clipboard_targets_received_cb, + terminal_screen_popup_info_ref (info)); +} + +static gboolean +screen_match_clicked_cb (TerminalScreen *screen, + const char *match, + int flavour, + guint state, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (screen != priv->active_screen) + return FALSE; + + switch (flavour) + { +#ifdef ENABLE_SKEY + case FLAVOR_SKEY: + terminal_skey_do_popup (GTK_WINDOW (window), screen, match); + break; +#endif + default: + gtk_widget_grab_focus (GTK_WIDGET (screen)); + terminal_util_open_url (GTK_WIDGET (window), match, flavour, + gtk_get_current_event_time ()); + break; + } + + return TRUE; +} + +static void +screen_close_cb (TerminalScreen *screen, + TerminalWindow *window) +{ + terminal_window_remove_screen (window, screen); +} + +static gboolean +terminal_window_accel_activate_cb (GtkAccelGroup *accel_group, + GObject *acceleratable, + guint keyval, + GdkModifierType modifier, + TerminalWindow *window) +{ + GtkAccelGroupEntry *entries; + guint n_entries; + gboolean retval = FALSE; + + entries = gtk_accel_group_query (accel_group, keyval, modifier, &n_entries); + if (n_entries > 0) + { + const char *accel_path; + + accel_path = g_quark_to_string (entries[0].accel_path_quark); + + if (g_str_has_prefix (accel_path, "<Actions>/Main/")) + { + const char *action_name; + + /* We want to always consume these accelerators, even if the corresponding + * action is insensitive, so the corresponding shortcut key escape code + * isn't sent to the terminal. See bug #453193, bug #138609 and bug #559728. + * This also makes tab cycling work, bug #92139. (NOT!) + */ + + action_name = I_(accel_path + strlen ("<Actions>/Main/")); + +#if 0 + if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)) > 1 && + (action_name == I_("TabsPrevious") || action_name == I_("TabsNext"))) + retval = TRUE; + else +#endif + if (action_name == I_("EditCopy") || + action_name == I_("PopupCopy") || + action_name == I_("EditPaste") || + action_name == I_("PopupPaste")) + retval = TRUE; + } + } + + return retval; +} + +/*****************************************/ + +#ifdef MATE_ENABLE_DEBUG +static void +terminal_window_size_request_cb (GtkWidget *widget, + GtkRequisition *req) +{ + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] size-request result %d : %d\n", + widget, req->width, req->height); +} + +static void +terminal_window_size_allocate_cb (GtkWidget *widget, + GtkAllocation *allocation) +{ + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] size-alloc result %d : %d at (%d, %d)\n", + widget, + allocation->width, allocation->height, + allocation->x, allocation->y); +} +#endif /* MATE_ENABLE_DEBUG */ + +static void +terminal_window_realize (GtkWidget *widget) +{ + TerminalWindow *window = TERMINAL_WINDOW (widget); + TerminalWindowPrivate *priv = window->priv; +#ifdef GDK_WINDOWING_X11 + GdkScreen *screen; + GdkColormap *colormap; + GtkAllocation widget_allocation; + + gtk_widget_get_allocation (widget, &widget_allocation); + screen = gtk_widget_get_screen (GTK_WIDGET (window)); + if (gdk_screen_is_composited (screen) && + (colormap = gdk_screen_get_rgba_colormap (screen)) != NULL) + { + /* Set RGBA colormap if possible so VTE can use real transparency */ + gtk_widget_set_colormap (widget, colormap); + priv->have_argb_visual = TRUE; + } + else + { + gtk_widget_set_colormap (widget, gdk_screen_get_default_colormap (screen)); + priv->have_argb_visual = FALSE; + } +#endif + + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] realize, size %d : %d at (%d, %d)\n", + widget, + widget_allocation.width, widget_allocation.height, + widget_allocation.x, widget_allocation.y); + + GTK_WIDGET_CLASS (terminal_window_parent_class)->realize (widget); + + /* Need to do this now since this requires the window to be realized */ + if (priv->active_screen != NULL) + sync_screen_icon_title (priv->active_screen, NULL, window); +} + +static gboolean +terminal_window_map_event (GtkWidget *widget, + GdkEventAny *event) +{ + TerminalWindow *window = TERMINAL_WINDOW (widget); + TerminalWindowPrivate *priv = window->priv; + gboolean (* map_event) (GtkWidget *, GdkEventAny *) = + GTK_WIDGET_CLASS (terminal_window_parent_class)->map_event; + GtkAllocation widget_allocation; + + gtk_widget_get_allocation (widget, &widget_allocation); + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] map-event, size %d : %d at (%d, %d)\n", + widget, + widget_allocation.width, widget_allocation.height, + widget_allocation.x, widget_allocation.y); + + if (priv->clear_demands_attention) + { +#ifdef GDK_WINDOWING_X11 + terminal_util_x11_clear_demands_attention (gtk_widget_get_window (widget)); +#endif + + priv->clear_demands_attention = FALSE; + } + + if (map_event) + return map_event (widget, event); + + return FALSE; +} + + +static gboolean +terminal_window_state_event (GtkWidget *widget, + GdkEventWindowState *event) +{ + gboolean (* window_state_event) (GtkWidget *, GdkEventWindowState *event) = + GTK_WIDGET_CLASS (terminal_window_parent_class)->window_state_event; + + if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) + { + TerminalWindow *window = TERMINAL_WINDOW (widget); + TerminalWindowPrivate *priv = window->priv; + GtkAction *action; + gboolean is_fullscreen; + + is_fullscreen = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0; + + action = gtk_action_group_get_action (priv->action_group, "ViewFullscreen"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), is_fullscreen); + + action = gtk_action_group_get_action (priv->action_group, "PopupLeaveFullscreen"); + gtk_action_set_visible (action, is_fullscreen); + } + + if (window_state_event) + return window_state_event (widget, event); + + return FALSE; +} + +static void +terminal_window_window_manager_changed_cb (GdkScreen *screen, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkAction *action; + gboolean supports_fs; + + supports_fs = gdk_x11_screen_supports_net_wm_hint (screen, gdk_atom_intern ("_NET_WM_STATE_FULLSCREEN", FALSE)); + + action = gtk_action_group_get_action (priv->action_group, "ViewFullscreen"); + gtk_action_set_sensitive (action, supports_fs); +} + +#ifdef GDK_WINDOWING_X11 + +static void +terminal_window_composited_changed_cb (GdkScreen *screen, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + gboolean composited; + + composited = gdk_screen_is_composited (screen); + if ((composited != priv->have_argb_visual) && + gtk_widget_get_realized (GTK_WIDGET (window))) + { + GtkWidget *widget = GTK_WIDGET (window); + GdkWindow *widget_window; + guint32 user_time; + gboolean have_desktop; + guint32 desktop = 0; /* Quiet GCC */ + gboolean was_minimized; + int x, y; + + widget_window = gtk_widget_get_window (widget); + + user_time = gdk_x11_display_get_user_time (gtk_widget_get_display (widget)); + + /* If compositing changed, re-realize the window. Bug #563561 */ + + /* Save the position; this isn't perfect, because the position + * that gtk_window_get_position() returns is the position of the + * frame window, and we are racing with the new window manager + * framing our window, so we might see a funky intermediate state. + * But at worst, we'll be off by a few pixels (the frame size). */ + gtk_window_get_position (GTK_WINDOW (window), &x, &y); + + /* GtkWindow tries to save whether the window was iconified + * and restore it, but that doesn't work because of problems + * GDK_WINDOW_STATE_ICONIFIED. For details, see comment for + * terminal_util_x11_window_is_minimized() + */ + was_minimized = terminal_util_x11_window_is_minimized (widget_window); + + /* And the desktop */ + have_desktop = terminal_util_x11_get_net_wm_desktop (widget_window, &desktop); + + gtk_widget_hide (widget); + gtk_widget_unrealize (widget); + + /* put the window back where it was before */ + gtk_window_move (GTK_WINDOW (window), x, y); + gtk_widget_realize (widget); + + /* Get new GdkWindow */ + widget_window = gtk_widget_get_window (widget); + + gdk_x11_window_set_user_time (widget_window, user_time); + + if (was_minimized) + gtk_window_iconify (GTK_WINDOW (window)); + else + gtk_window_deiconify (GTK_WINDOW (window)); + + gtk_widget_show (widget); + if (have_desktop) + terminal_util_x11_set_net_wm_desktop (widget_window, desktop); + + /* Mapping the window is likely to have set the "demands-attention" state. + * In particular, Marco will always set the state if a window is mapped, + * is not given the focus (because of an old user time), and is covered + * by some other window. We have no way of preventing this, so we just + * wait for our window to be mapped, and then tell the window manager + * to turn off the bit. If it wasn't set, no harm. + */ + priv->clear_demands_attention = TRUE; + } +} + +#endif /* GDK_WINDOWING_X11 */ + +static void +terminal_window_screen_update (TerminalWindow *window, + GdkScreen *screen) +{ + TerminalApp *app; + + terminal_window_window_manager_changed_cb (screen, window); + g_signal_connect (screen, "window-manager-changed", + G_CALLBACK (terminal_window_window_manager_changed_cb), window); +#ifdef GDK_WINDOWING_X11 + g_signal_connect (screen, "composited-changed", + G_CALLBACK (terminal_window_composited_changed_cb), window); +#endif + + if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (screen), "GT::HasSettingsConnection"))) + return; + + g_object_set_data_full (G_OBJECT (screen), "GT::HasSettingsConnection", + GINT_TO_POINTER (TRUE), + (GDestroyNotify) app_setting_notify_destroy_cb); + + app = terminal_app_get (); + app_setting_notify_cb (app, NULL, screen); + g_signal_connect (app, "notify::" TERMINAL_APP_ENABLE_MNEMONICS, + G_CALLBACK (app_setting_notify_cb), screen); + g_signal_connect (app, "notify::" TERMINAL_APP_ENABLE_MENU_BAR_ACCEL, + G_CALLBACK (app_setting_notify_cb), screen); +} + +static void +terminal_window_screen_changed (GtkWidget *widget, + GdkScreen *previous_screen) +{ + TerminalWindow *window = TERMINAL_WINDOW (widget); + void (* screen_changed) (GtkWidget *, GdkScreen *) = + GTK_WIDGET_CLASS (terminal_window_parent_class)->screen_changed; + GdkScreen *screen; + + if (screen_changed) + screen_changed (widget, previous_screen); + + screen = gtk_widget_get_screen (widget); + if (previous_screen == screen) + return; + + if (previous_screen) + { + g_signal_handlers_disconnect_by_func (previous_screen, + G_CALLBACK (terminal_window_window_manager_changed_cb), + window); +#ifdef GDK_WINDOWING_X11 + g_signal_handlers_disconnect_by_func (previous_screen, + G_CALLBACK (terminal_window_composited_changed_cb), + window); +#endif + } + + if (!screen) + return; + + terminal_window_screen_update (window, screen); +} + +static void +terminal_window_profile_list_changed_cb (TerminalApp *app, + TerminalWindow *window) +{ + terminal_window_update_set_profile_menu (window); + terminal_window_update_new_terminal_menus (window); +} + +static void +terminal_window_encoding_list_changed_cb (TerminalApp *app, + TerminalWindow *window) +{ + terminal_window_update_encoding_menu (window); +} + +static void +terminal_window_init (TerminalWindow *window) +{ + const GtkActionEntry menu_entries[] = + { + /* Toplevel */ + { "File", NULL, N_("_File") }, + { "FileNewWindowProfiles", STOCK_NEW_WINDOW, N_("Open _Terminal")}, + { "FileNewTabProfiles", STOCK_NEW_TAB, N_("Open Ta_b") }, + { "Edit", NULL, N_("_Edit") }, + { "View", NULL, N_("_View") }, + { "Search", NULL, N_("_Search") }, + { "Terminal", NULL, N_("_Terminal") }, + { "Tabs", NULL, N_("Ta_bs") }, + { "Help", NULL, N_("_Help") }, + { "Popup", NULL, NULL }, + { "NotebookPopup", NULL, "" }, + + /* File menu */ + { "FileNewWindow", STOCK_NEW_WINDOW, N_("Open _Terminal"), "<shift><control>N", + NULL, + G_CALLBACK (file_new_window_callback) }, + { "FileNewTab", STOCK_NEW_TAB, N_("Open Ta_b"), "<shift><control>T", + NULL, + G_CALLBACK (file_new_tab_callback) }, + { "FileNewProfile", GTK_STOCK_OPEN, N_("New _Profile…"), "", + NULL, + G_CALLBACK (file_new_profile_callback) }, + { "FileSaveContents", GTK_STOCK_SAVE, N_("_Save Contents"), "", + NULL, + G_CALLBACK (file_save_contents_callback) }, + { "FileCloseTab", GTK_STOCK_CLOSE, N_("C_lose Tab"), "<shift><control>W", + NULL, + G_CALLBACK (file_close_tab_callback) }, + { "FileCloseWindow", GTK_STOCK_CLOSE, N_("_Close Window"), "<shift><control>Q", + NULL, + G_CALLBACK (file_close_window_callback) }, + + /* Edit menu */ + { "EditCopy", GTK_STOCK_COPY, NULL, "<shift><control>C", + NULL, + G_CALLBACK (edit_copy_callback) }, + { "EditPaste", GTK_STOCK_PASTE, NULL, "<shift><control>V", + NULL, + G_CALLBACK (edit_paste_callback) }, + { "EditPasteURIPaths", GTK_STOCK_PASTE, N_("Paste _Filenames"), "", + NULL, + G_CALLBACK (edit_paste_callback) }, + { "EditSelectAll", GTK_STOCK_SELECT_ALL, NULL, NULL, + NULL, + G_CALLBACK (edit_select_all_callback) }, + { "EditProfiles", NULL, N_("P_rofiles…"), NULL, + NULL, + G_CALLBACK (edit_profiles_callback) }, + { "EditKeybindings", NULL, N_("_Keyboard Shortcuts…"), NULL, + NULL, + G_CALLBACK (edit_keybindings_callback) }, + { "EditCurrentProfile", NULL, N_("Pr_ofile Preferences"), NULL, + NULL, + G_CALLBACK (edit_current_profile_callback) }, + + /* View menu */ + { "ViewZoomIn", GTK_STOCK_ZOOM_IN, NULL, "<control>plus", + NULL, + G_CALLBACK (view_zoom_in_callback) }, + { "ViewZoomOut", GTK_STOCK_ZOOM_OUT, NULL, "<control>minus", + NULL, + G_CALLBACK (view_zoom_out_callback) }, + { "ViewZoom100", GTK_STOCK_ZOOM_100, NULL, "<control>0", + NULL, + G_CALLBACK (view_zoom_normal_callback) }, + + /* Search menu */ + { "SearchFind", GTK_STOCK_FIND, N_("_Find..."), "<shift><control>F", + NULL, + G_CALLBACK (search_find_callback) }, + { "SearchFindNext", NULL, N_("Find Ne_xt"), "<shift><control>H", + NULL, + G_CALLBACK (search_find_next_callback) }, + { "SearchFindPrevious", NULL, N_("Find Pre_vious"), "<shift><control>G", + NULL, + G_CALLBACK (search_find_prev_callback) }, + { "SearchClearHighlight", NULL, N_("_Clear Highlight"), "<shift><control>J", + NULL, + G_CALLBACK (search_clear_highlight_callback) }, +#if 0 + { "SearchGoToLine", GTK_STOCK_JUMP_TO, N_("Go to _Line..."), "<shift><control>I", + NULL, + G_CALLBACK (search_goto_line_callback) }, + { "SearchIncrementalSearch", GTK_STOCK_FIND, N_("_Incremental Search..."), "<shift><control>K", + NULL, + G_CALLBACK (search_incremental_search_callback) }, +#endif + + /* Terminal menu */ + { "TerminalProfiles", NULL, N_("Change _Profile") }, + { "TerminalSetTitle", NULL, N_("_Set Title…"), NULL, + NULL, + G_CALLBACK (terminal_set_title_callback) }, + { "TerminalSetEncoding", NULL, N_("Set _Character Encoding") }, + { "TerminalReset", NULL, N_("_Reset"), NULL, + NULL, + G_CALLBACK (terminal_reset_callback) }, + { "TerminalResetClear", NULL, N_("Reset and C_lear"), NULL, + NULL, + G_CALLBACK (terminal_reset_clear_callback) }, + + /* Terminal/Encodings menu */ + { "TerminalAddEncoding", NULL, N_("_Add or Remove…"), NULL, + NULL, + G_CALLBACK (terminal_add_encoding_callback) }, + + /* Tabs menu */ + { "TabsPrevious", NULL, N_("_Previous Tab"), "<control>Page_Up", + NULL, + G_CALLBACK (tabs_next_or_previous_tab_cb) }, + { "TabsNext", NULL, N_("_Next Tab"), "<control>Page_Down", + NULL, + G_CALLBACK (tabs_next_or_previous_tab_cb) }, + { "TabsMoveLeft", NULL, N_("Move Tab _Left"), "<shift><control>Page_Up", + NULL, + G_CALLBACK (tabs_move_left_callback) }, + { "TabsMoveRight", NULL, N_("Move Tab _Right"), "<shift><control>Page_Down", + NULL, + G_CALLBACK (tabs_move_right_callback) }, + { "TabsDetach", NULL, N_("_Detach tab"), NULL, + NULL, + G_CALLBACK (tabs_detach_tab_callback) }, + + /* Help menu */ + { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1", + NULL, + G_CALLBACK (help_contents_callback) }, + { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL, + NULL, + G_CALLBACK (help_about_callback) }, + + /* Popup menu */ + { "PopupSendEmail", NULL, N_("_Send Mail To…"), NULL, + NULL, + G_CALLBACK (popup_open_url_callback) }, + { "PopupCopyEmailAddress", NULL, N_("_Copy E-mail Address"), NULL, + NULL, + G_CALLBACK (popup_copy_url_callback) }, + { "PopupCall", NULL, N_("C_all To…"), NULL, + NULL, + G_CALLBACK (popup_open_url_callback) }, + { "PopupCopyCallAddress", NULL, N_("_Copy Call Address"), NULL, + NULL, + G_CALLBACK (popup_copy_url_callback) }, + { "PopupOpenLink", NULL, N_("_Open Link"), NULL, + NULL, + G_CALLBACK (popup_open_url_callback) }, + { "PopupCopyLinkAddress", NULL, N_("_Copy Link Address"), NULL, + NULL, + G_CALLBACK (popup_copy_url_callback) }, + { "PopupTerminalProfiles", NULL, N_("P_rofiles") }, + { "PopupCopy", GTK_STOCK_COPY, NULL, "", + NULL, + G_CALLBACK (edit_copy_callback) }, + { "PopupPaste", GTK_STOCK_PASTE, NULL, "", + NULL, + G_CALLBACK (edit_paste_callback) }, + { "PopupPasteURIPaths", GTK_STOCK_PASTE, N_("Paste _Filenames"), "", + NULL, + G_CALLBACK (edit_paste_callback) }, + { "PopupNewTerminal", NULL, N_("Open _Terminal"), NULL, + NULL, + G_CALLBACK (file_new_window_callback) }, + { "PopupNewTab", NULL, N_("Open Ta_b"), NULL, + NULL, + G_CALLBACK (file_new_tab_callback) }, + { "PopupCloseWindow", NULL, N_("C_lose Window"), NULL, + NULL, + G_CALLBACK (file_close_window_callback) }, + { "PopupCloseTab", NULL, N_("C_lose Tab"), NULL, + NULL, + G_CALLBACK (file_close_tab_callback) }, + { "PopupLeaveFullscreen", NULL, N_("L_eave Full Screen"), NULL, + NULL, + G_CALLBACK (popup_leave_fullscreen_callback) }, + { "PopupInputMethods", NULL, N_("_Input Methods") } + }; + + const GtkToggleActionEntry toggle_menu_entries[] = + { + /* View Menu */ + { "ViewMenubar", NULL, N_("Show _Menubar"), NULL, + NULL, + G_CALLBACK (view_menubar_toggled_callback), + FALSE }, + { "ViewFullscreen", NULL, N_("_Full Screen"), NULL, + NULL, + G_CALLBACK (view_fullscreen_toggled_callback), + FALSE } + }; + TerminalWindowPrivate *priv; + TerminalApp *app; + GtkActionGroup *action_group; + GtkAction *action; + GtkUIManager *manager; + GtkWidget *main_vbox; + GError *error; + GtkWindowGroup *window_group; + GtkAccelGroup *accel_group; + + priv = window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, TERMINAL_TYPE_WINDOW, TerminalWindowPrivate); + + g_signal_connect (G_OBJECT (window), "delete_event", + G_CALLBACK(terminal_window_delete_event), + NULL); +#ifdef MATE_ENABLE_DEBUG + _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_GEOMETRY) + { + g_signal_connect_after (window, "size-request", G_CALLBACK (terminal_window_size_request_cb), NULL); + g_signal_connect_after (window, "size-allocate", G_CALLBACK (terminal_window_size_allocate_cb), NULL); + } +#endif + + gtk_window_set_title (GTK_WINDOW (window), _("Terminal")); + + priv->active_screen = NULL; + priv->menubar_visible = FALSE; + + main_vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), main_vbox); + gtk_widget_show (main_vbox); + + priv->notebook = gtk_notebook_new (); + gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->notebook), FALSE); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE); + gtk_notebook_set_group (GTK_NOTEBOOK (priv->notebook), GUINT_TO_POINTER (1)); + gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), + TRUE); + g_signal_connect (priv->notebook, "button-press-event", + G_CALLBACK (notebook_button_press_cb), window); + g_signal_connect (priv->notebook, "popup-menu", + G_CALLBACK (notebook_popup_menu_cb), window); + g_signal_connect_after (priv->notebook, "switch-page", + G_CALLBACK (notebook_page_selected_callback), window); + g_signal_connect_after (priv->notebook, "page-added", + G_CALLBACK (notebook_page_added_callback), window); + g_signal_connect_after (priv->notebook, "page-removed", + G_CALLBACK (notebook_page_removed_callback), window); + g_signal_connect_data (priv->notebook, "page-reordered", + G_CALLBACK (terminal_window_update_tabs_menu_sensitivity), + window, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER); + + gtk_box_pack_end (GTK_BOX (main_vbox), priv->notebook, TRUE, TRUE, 0); + gtk_widget_show (priv->notebook); + + priv->old_char_width = -1; + priv->old_char_height = -1; + priv->old_geometry_widget = NULL; + + /* Create the UI manager */ + manager = priv->ui_manager = gtk_ui_manager_new (); + + accel_group = gtk_ui_manager_get_accel_group (manager); + gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); + /* Workaround for bug #453193, bug #138609 and bug #559728 */ + g_signal_connect_after (accel_group, "accel-activate", + G_CALLBACK (terminal_window_accel_activate_cb), window); + + /* Create the actions */ + /* Note that this action group name is used in terminal-accels.c; do not change it */ + priv->action_group = action_group = gtk_action_group_new ("Main"); + gtk_action_group_set_translation_domain (action_group, NULL); + gtk_action_group_add_actions (action_group, menu_entries, + G_N_ELEMENTS (menu_entries), window); + gtk_action_group_add_toggle_actions (action_group, + toggle_menu_entries, + G_N_ELEMENTS (toggle_menu_entries), + window); + gtk_ui_manager_insert_action_group (manager, action_group, 0); + g_object_unref (action_group); + + action = gtk_action_group_get_action (action_group, "Edit"); + g_signal_connect (action, "activate", + G_CALLBACK (edit_menu_activate_callback), window); + + /* Set this action invisible so the Edit menu doesn't flash the first + * time it's shown and there's no text/uri-list on the clipboard. + */ + action = gtk_action_group_get_action (priv->action_group, "EditPasteURIPaths"); + gtk_action_set_visible (action, FALSE); + + /* Idem for this action, since the window is not fullscreen. */ + action = gtk_action_group_get_action (priv->action_group, "PopupLeaveFullscreen"); + gtk_action_set_visible (action, FALSE); + +#ifndef ENABLE_SAVE + action = gtk_action_group_get_action (priv->action_group, "FileSaveContents"); + gtk_action_set_visible (action, FALSE); +#endif + + /* Load the UI */ + error = NULL; + priv->ui_id = gtk_ui_manager_add_ui_from_file (manager, + TERM_PKGDATADIR G_DIR_SEPARATOR_S "terminal.xml", + &error); + if (error) + { + g_printerr ("Failed to load UI: %s\n", error->message); + g_error_free (error); + } + + priv->menubar = gtk_ui_manager_get_widget (manager, "/menubar"); + gtk_box_pack_start (GTK_BOX (main_vbox), + priv->menubar, + FALSE, FALSE, 0); + + /* Add tabs menu */ + priv->tabs_menu = terminal_tabs_menu_new (window); + + app = terminal_app_get (); + terminal_window_profile_list_changed_cb (app, window); + g_signal_connect (app, "profile-list-changed", + G_CALLBACK (terminal_window_profile_list_changed_cb), window); + + terminal_window_encoding_list_changed_cb (app, window); + g_signal_connect (app, "encoding-list-changed", + G_CALLBACK (terminal_window_encoding_list_changed_cb), window); + + terminal_window_set_menubar_visible (window, TRUE); + priv->use_default_menubar_visibility = TRUE; + + terminal_window_update_size_to_menu (window); + + /* We have to explicitly call this, since screen-changed is NOT + * emitted for the toplevel the first time! + */ + terminal_window_screen_update (window, gtk_widget_get_screen (GTK_WIDGET (window))); + + window_group = gtk_window_group_new (); + gtk_window_group_add_window (window_group, GTK_WINDOW (window)); + g_object_unref (window_group); + + terminal_util_set_unique_role (GTK_WINDOW (window), "mate-terminal-window"); +} + +static void +terminal_window_class_init (TerminalWindowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = terminal_window_dispose; + object_class->finalize = terminal_window_finalize; + + widget_class->show = terminal_window_show; + widget_class->realize = terminal_window_realize; + widget_class->map_event = terminal_window_map_event; + widget_class->window_state_event = terminal_window_state_event; + widget_class->screen_changed = terminal_window_screen_changed; + + g_type_class_add_private (object_class, sizeof (TerminalWindowPrivate)); + + gtk_rc_parse_string ("style \"mate-terminal-tab-close-button-style\"\n" + "{\n" + "GtkWidget::focus-padding = 0\n" + "GtkWidget::focus-line-width = 0\n" + "xthickness = 0\n" + "ythickness = 0\n" + "}\n" + "widget \"*.mate-terminal-tab-close-button\" style \"mate-terminal-tab-close-button-style\""); + + gtk_notebook_set_window_creation_hook (handle_tab_droped_on_desktop, NULL, NULL); +} + +static void +terminal_window_dispose (GObject *object) +{ + TerminalWindow *window = TERMINAL_WINDOW (object); + TerminalWindowPrivate *priv = window->priv; + TerminalApp *app; + GdkScreen *screen; + + remove_popup_info (window); + + priv->disposed = TRUE; + + if (priv->tabs_menu) + { + g_object_unref (priv->tabs_menu); + priv->tabs_menu = NULL; + } + + if (priv->profiles_action_group != NULL) + disconnect_profiles_from_actions_in_group (priv->profiles_action_group); + if (priv->new_terminal_action_group != NULL) + disconnect_profiles_from_actions_in_group (priv->new_terminal_action_group); + + app = terminal_app_get (); + g_signal_handlers_disconnect_by_func (app, + G_CALLBACK (terminal_window_profile_list_changed_cb), + window); + g_signal_handlers_disconnect_by_func (app, + G_CALLBACK (terminal_window_encoding_list_changed_cb), + window); + + screen = gtk_widget_get_screen (GTK_WIDGET (object)); + if (screen) + { + g_signal_handlers_disconnect_by_func (screen, + G_CALLBACK (terminal_window_window_manager_changed_cb), + window); +#ifdef GDK_WINDOWING_X11 + g_signal_handlers_disconnect_by_func (screen, + G_CALLBACK (terminal_window_composited_changed_cb), + window); +#endif + } + + G_OBJECT_CLASS (terminal_window_parent_class)->dispose (object); +} + +static void +terminal_window_finalize (GObject *object) +{ + TerminalWindow *window = TERMINAL_WINDOW (object); + TerminalWindowPrivate *priv = window->priv; + + g_object_unref (priv->ui_manager); + + if (priv->confirm_close_dialog) + gtk_dialog_response (GTK_DIALOG (priv->confirm_close_dialog), + GTK_RESPONSE_DELETE_EVENT); + + if (priv->search_find_dialog) + gtk_dialog_response (GTK_DIALOG (priv->search_find_dialog), + GTK_RESPONSE_DELETE_EVENT); + + G_OBJECT_CLASS (terminal_window_parent_class)->finalize (object); +} + +static gboolean +terminal_window_delete_event (GtkWidget *widget, + GdkEvent *event, + gpointer data) +{ + return confirm_close_window_or_tab (TERMINAL_WINDOW (widget), NULL); +} + +static void +terminal_window_show (GtkWidget *widget) +{ + TerminalWindow *window = TERMINAL_WINDOW (widget); + GtkAllocation widget_allocation; + + gtk_widget_get_allocation (widget, &widget_allocation); + +#if 0 + TerminalWindowPrivate *priv = window->priv; + + if (priv->active_screen != NULL) + { + /* At this point, we have our GdkScreen, and hence the right + * font size, so we can go ahead and size the window. */ + terminal_window_set_size (window, priv->active_screen, FALSE); + } +#endif + + terminal_window_update_geometry (window); + + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] show, size %d : %d at (%d, %d)\n", + widget, + widget_allocation.width, widget_allocation.height, + widget_allocation.x, widget_allocation.y); + + GTK_WIDGET_CLASS (terminal_window_parent_class)->show (widget); +} + +TerminalWindow* +terminal_window_new (void) +{ + return g_object_new (TERMINAL_TYPE_WINDOW, NULL); +} + +/** + * terminal_window_set_is_restored: + * @window: + * + * Marks the window as restored from session. + */ +void +terminal_window_set_is_restored (TerminalWindow *window) +{ + g_return_if_fail (TERMINAL_IS_WINDOW (window)); + g_return_if_fail (!gtk_widget_get_mapped (GTK_WIDGET (window))); + + window->priv->clear_demands_attention = TRUE; +} + +static void +profile_set_callback (TerminalScreen *screen, + TerminalProfile *old_profile, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (!gtk_widget_get_realized (GTK_WIDGET (window))) + return; + + if (screen != priv->active_screen) + return; + + terminal_window_update_set_profile_menu_active_profile (window); +} + +static void +sync_screen_title (TerminalScreen *screen, + GParamSpec *psepc, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (screen != priv->active_screen) + return; + + gtk_window_set_title (GTK_WINDOW (window), terminal_screen_get_title (screen)); +} + +static void +sync_screen_icon_title (TerminalScreen *screen, + GParamSpec *psepc, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (!gtk_widget_get_realized (GTK_WIDGET (window))) + return; + + if (screen != priv->active_screen) + return; + + if (!terminal_screen_get_icon_title_set (screen)) + return; + + gdk_window_set_icon_name (gtk_widget_get_window (GTK_WIDGET (window)), terminal_screen_get_icon_title (screen)); + + priv->icon_title_set = TRUE; +} + +static void +sync_screen_icon_title_set (TerminalScreen *screen, + GParamSpec *psepc, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (!gtk_widget_get_realized (GTK_WIDGET (window))) + return; + + /* No need to restore the title if we never set an icon title */ + if (!priv->icon_title_set) + return; + + if (screen != priv->active_screen) + return; + + if (terminal_screen_get_icon_title_set (screen)) + return; + + /* Need to reset the icon name */ + /* FIXME: Once gtk+ bug 535557 is fixed, use that to unset the icon title. */ + + g_object_set_qdata (G_OBJECT (gtk_widget_get_window (GTK_WIDGET (window))), + g_quark_from_static_string ("gdk-icon-name-set"), + GUINT_TO_POINTER (FALSE)); + priv->icon_title_set = FALSE; + + /* Re-setting the right title will be done by the notify::title handler which comes after this one */ +} + +/* Notebook callbacks */ + +static void +close_button_clicked_cb (GtkWidget *tab_label, + GtkWidget *screen_container) +{ + GtkWidget *toplevel; + TerminalWindow *window; + TerminalWindowPrivate *priv; + TerminalScreen *screen; + + toplevel = gtk_widget_get_toplevel (screen_container); + if (!gtk_widget_is_toplevel (toplevel)) + return; + + if (!TERMINAL_IS_WINDOW (toplevel)) + return; + + window = TERMINAL_WINDOW (toplevel); + priv = window->priv; + + screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (screen_container)); + if (confirm_close_window_or_tab (window, screen)) + return; + + terminal_window_remove_screen (window, screen); +} + +void +terminal_window_add_screen (TerminalWindow *window, + TerminalScreen *screen, + int position) +{ + TerminalWindowPrivate *priv = window->priv; + GtkWidget *old_window; + GtkWidget *screen_container, *tab_label; + + old_window = gtk_widget_get_toplevel (GTK_WIDGET (screen)); + if (gtk_widget_is_toplevel (old_window) && + TERMINAL_IS_WINDOW (old_window) && + TERMINAL_WINDOW (old_window)== window) + return; + + if (TERMINAL_IS_WINDOW (old_window)) + terminal_window_remove_screen (TERMINAL_WINDOW (old_window), screen); + + screen_container = terminal_screen_container_new (screen); + gtk_widget_show (screen_container); + + update_tab_visibility (window, +1); + + tab_label = terminal_tab_label_new (screen); + g_signal_connect (tab_label, "close-button-clicked", + G_CALLBACK (close_button_clicked_cb), screen_container); + + gtk_notebook_insert_page (GTK_NOTEBOOK (priv->notebook), + screen_container, + tab_label, + position); + gtk_container_child_set (GTK_CONTAINER (priv->notebook), + screen_container, + "tab-expand", TRUE, + "tab-fill", TRUE, + NULL); + gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook), + screen_container, + TRUE); + gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook), + screen_container, + TRUE); +} + +void +terminal_window_remove_screen (TerminalWindow *window, + TerminalScreen *screen) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalScreenContainer *screen_container; + + g_return_if_fail (gtk_widget_get_toplevel (GTK_WIDGET (screen)) == GTK_WIDGET (window)); + + update_tab_visibility (window, -1); + + screen_container = terminal_screen_container_get_from_screen (screen); + gtk_container_remove (GTK_CONTAINER (priv->notebook), + GTK_WIDGET (screen_container)); +} + +void +terminal_window_move_screen (TerminalWindow *source_window, + TerminalWindow *dest_window, + TerminalScreen *screen, + int dest_position) +{ + TerminalScreenContainer *screen_container; + + g_return_if_fail (TERMINAL_IS_WINDOW (source_window)); + g_return_if_fail (TERMINAL_IS_WINDOW (dest_window)); + g_return_if_fail (TERMINAL_IS_SCREEN (screen)); + g_return_if_fail (gtk_widget_get_toplevel (GTK_WIDGET (screen)) == GTK_WIDGET (source_window)); + g_return_if_fail (dest_position >= -1); + + screen_container = terminal_screen_container_get_from_screen (screen); + g_assert (TERMINAL_IS_SCREEN_CONTAINER (screen_container)); + + /* We have to ref the screen container as well as the screen, + * because otherwise removing the screen container from the source + * window's notebook will cause the container and its containing + * screen to be gtk_widget_destroy()ed! + */ + g_object_ref_sink (screen_container); + g_object_ref_sink (screen); + terminal_window_remove_screen (source_window, screen); + + /* Now we can safely remove the screen from the container and let the container die */ + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (screen))), GTK_WIDGET (screen)); + g_object_unref (screen_container); + + terminal_window_add_screen (dest_window, screen, dest_position); + g_object_unref (screen); +} + +GList* +terminal_window_list_screen_containers (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + /* We are trusting that GtkNotebook will return pages in order */ + return gtk_container_get_children (GTK_CONTAINER (priv->notebook)); +} + +void +terminal_window_set_menubar_visible (TerminalWindow *window, + gboolean setting) +{ + TerminalWindowPrivate *priv = window->priv; + GtkAction *action; + + /* it's been set now, so don't override when adding a screen. + * this side effect must happen before we short-circuit below. + */ + priv->use_default_menubar_visibility = FALSE; + + if (setting == priv->menubar_visible) + return; + + priv->menubar_visible = setting; + + action = gtk_action_group_get_action (priv->action_group, "ViewMenubar"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), setting); + + g_object_set (priv->menubar, "visible", setting, NULL); + + /* FIXMEchpe: use gtk_widget_get_realized instead? */ + if (priv->active_screen) + { + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] setting size after toggling menubar visibility\n", + window); + + terminal_window_set_size (window, priv->active_screen, TRUE); + } +} + +gboolean +terminal_window_get_menubar_visible (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + return priv->menubar_visible; +} + +GtkWidget * +terminal_window_get_notebook (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + g_return_val_if_fail (TERMINAL_IS_WINDOW (window), NULL); + + return GTK_WIDGET (priv->notebook); +} + +void +terminal_window_set_size (TerminalWindow *window, + TerminalScreen *screen, + gboolean even_if_mapped) +{ + terminal_window_set_size_force_grid (window, screen, even_if_mapped, -1, -1); +} + +void +terminal_window_set_size_force_grid (TerminalWindow *window, + TerminalScreen *screen, + gboolean even_if_mapped, + int force_grid_width, + int force_grid_height) +{ + /* Owen's hack from mate-terminal */ + GtkWidget *widget; + GtkWidget *app; + GtkRequisition toplevel_request; + GtkRequisition widget_request; + int w, h; + int char_width; + int char_height; + int grid_width; + int grid_height; + GtkBorder *inner_border = NULL; + + /* be sure our geometry is up-to-date */ + terminal_window_update_geometry (window); + + widget = GTK_WIDGET (screen); + + app = gtk_widget_get_toplevel (widget); + g_assert (app != NULL); + + gtk_widget_size_request (app, &toplevel_request); + gtk_widget_size_request (widget, &widget_request); + + terminal_screen_get_cell_size (screen, &char_width, &char_height); + terminal_screen_get_size (screen, &grid_width, &grid_height); + + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] set size: toplevel %dx%d widget %dx%d grid %dx%d char-cell %dx%d\n", + window, + toplevel_request.width, toplevel_request.height, + widget_request.width, widget_request.height, + grid_width, grid_height, char_width, char_height); + + w = toplevel_request.width - widget_request.width; + h = toplevel_request.height - widget_request.height; + + if (force_grid_width >= 0) + grid_width = force_grid_width; + if (force_grid_height >= 0) + grid_height = force_grid_height; + + gtk_widget_style_get (widget, "inner-border", &inner_border, NULL); + w += (inner_border ? (inner_border->left + inner_border->right) : 0) + char_width * grid_width; + h += (inner_border ? (inner_border->top + inner_border->bottom) : 0) + char_height * grid_height; + gtk_border_free (inner_border); + + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] set size: grid %dx%d force %dx%d setting %dx%d pixels\n", + window, + grid_width, grid_height, force_grid_width, force_grid_height, w, h); + + if (even_if_mapped && gtk_widget_get_mapped (app)) { + gtk_window_resize (GTK_WINDOW (app), w, h); + } + else { + gtk_window_set_default_size (GTK_WINDOW (app), w, h); + } +} + +void +terminal_window_switch_screen (TerminalWindow *window, + TerminalScreen *screen) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalScreenContainer *screen_container; + int page_num; + + screen_container = terminal_screen_container_get_from_screen (screen); + g_assert (TERMINAL_IS_SCREEN_CONTAINER (screen_container)); + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook), + GTK_WIDGET (screen_container)); + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page_num); +} + +TerminalScreen* +terminal_window_get_active (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + return priv->active_screen; +} + +static gboolean +notebook_button_press_cb (GtkWidget *widget, + GdkEventButton *event, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkNotebook *notebook = GTK_NOTEBOOK (widget); + GtkWidget *menu; + GtkAction *action; + int tab_clicked; + + if (event->type != GDK_BUTTON_PRESS || + event->button != 3 || + (event->state & gtk_accelerator_get_default_mod_mask ()) != 0) + return FALSE; + + tab_clicked = find_tab_num_at_pos (notebook, event->x_root, event->y_root); + if (tab_clicked < 0) + return FALSE; + + /* switch to the page the mouse is over */ + gtk_notebook_set_current_page (notebook, tab_clicked); + + action = gtk_action_group_get_action (priv->action_group, "NotebookPopup"); + gtk_action_activate (action); + + menu = gtk_ui_manager_get_widget (priv->ui_manager, "/NotebookPopup"); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, + NULL, NULL, + event->button, event->time); + + return TRUE; +} + +static gboolean +notebook_popup_menu_cb (GtkWidget *widget, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkNotebook *notebook = GTK_NOTEBOOK (priv->notebook); + GtkWidget *focus_widget, *tab, *tab_label, *menu; + GtkAction *action; + int page_num; + + focus_widget = gtk_window_get_focus (GTK_WINDOW (window)); + /* Only respond if the notebook is the actual focus */ + if (focus_widget != priv->notebook) + return FALSE; + + page_num = gtk_notebook_get_current_page (notebook); + tab = gtk_notebook_get_nth_page (notebook, page_num); + tab_label = gtk_notebook_get_tab_label (notebook, tab); + + action = gtk_action_group_get_action (priv->action_group, "NotebookPopup"); + gtk_action_activate (action); + + menu = gtk_ui_manager_get_widget (priv->ui_manager, "/NotebookPopup"); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, + position_menu_under_widget, tab_label, + 0, gtk_get_current_event_time ()); + gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE); + + return TRUE; +} + +static void +notebook_page_selected_callback (GtkWidget *notebook, +#if GTK_CHECK_VERSION (2, 90, 6) + GtkWidget *page_widget, +#else + gpointer useless_crap, +#endif + guint page_num, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkWidget *widget; + TerminalScreen *screen; + TerminalProfile *profile; + int old_grid_width, old_grid_height; +#if !GTK_CHECK_VERSION (2, 90, 6) + GtkWidget *page_widget; +#endif + + _terminal_debug_print (TERMINAL_DEBUG_MDI, + "[window %p] MDI: page-selected %d\n", + window, page_num); + + if (priv->disposed) + return; + +#if !GTK_CHECK_VERSION (2, 90, 6) + page_widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), page_num); +#endif + + screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (page_widget)); + widget = GTK_WIDGET (screen); + g_assert (screen != NULL); + + _terminal_debug_print (TERMINAL_DEBUG_MDI, + "[window %p] MDI: setting active tab to screen %p (old active screen %p)\n", + window, screen, priv->active_screen); + + if (priv->active_screen == screen) + return; + + if (priv->active_screen != NULL) { + terminal_screen_get_size (priv->active_screen, &old_grid_width, &old_grid_height); + + /* This is so that we maintain the same grid */ + vte_terminal_set_size (VTE_TERMINAL (screen), old_grid_width, old_grid_height); + } + + /* Workaround to remove gtknotebook's feature of computing its size based on + * all pages. When the widget is hidden, its size will not be taken into + * account. + */ + if (priv->active_screen) + gtk_widget_hide (GTK_WIDGET (priv->active_screen)); /* FIXME */ + + /* Make sure that the widget is no longer hidden due to the workaround */ + gtk_widget_show (widget); + + profile = terminal_screen_get_profile (screen); + + priv->active_screen = screen; + + /* Override menubar setting if it wasn't restored from session */ + if (priv->use_default_menubar_visibility) + { + gboolean setting = + terminal_profile_get_property_boolean (terminal_screen_get_profile (screen), TERMINAL_PROFILE_DEFAULT_SHOW_MENUBAR); + + terminal_window_set_menubar_visible (window, setting); + } + + sync_screen_icon_title_set (screen, NULL, window); + sync_screen_icon_title (screen, NULL, window); + sync_screen_title (screen, NULL, window); + + /* set size of window to current grid size */ + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] setting size after flipping notebook pages\n", + window); + terminal_window_set_size (window, screen, TRUE); + + terminal_window_update_tabs_menu_sensitivity (window); + terminal_window_update_encoding_menu_active_encoding (window); + terminal_window_update_set_profile_menu_active_profile (window); + terminal_window_update_copy_sensitivity (screen, window); + terminal_window_update_zoom_sensitivity (window); + terminal_window_update_search_sensitivity (screen, window); +} + +static void +notebook_page_added_callback (GtkWidget *notebook, + GtkWidget *container, + guint page_num, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalScreen *screen; + + screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (container)); + + _terminal_debug_print (TERMINAL_DEBUG_MDI, + "[window %p] MDI: screen %p inserted\n", + window, screen); + + g_signal_connect (G_OBJECT (screen), + "profile-set", + G_CALLBACK (profile_set_callback), + window); + + /* FIXME: only connect on the active screen, not all screens! */ + g_signal_connect (screen, "notify::title", + G_CALLBACK (sync_screen_title), window); + g_signal_connect (screen, "notify::icon-title", + G_CALLBACK (sync_screen_icon_title), window); + g_signal_connect (screen, "notify::icon-title-set", + G_CALLBACK (sync_screen_icon_title_set), window); + g_signal_connect (screen, "selection-changed", + G_CALLBACK (terminal_window_update_copy_sensitivity), window); + + g_signal_connect (screen, "show-popup-menu", + G_CALLBACK (screen_show_popup_menu_callback), window); + g_signal_connect (screen, "match-clicked", + G_CALLBACK (screen_match_clicked_cb), window); + g_signal_connect (screen, "resize-window", + G_CALLBACK (screen_resize_window_cb), window); + + g_signal_connect (screen, "close-screen", + G_CALLBACK (screen_close_cb), window); + + update_tab_visibility (window, 0); + terminal_window_update_tabs_menu_sensitivity (window); + terminal_window_update_search_sensitivity (screen, window); + +#if 0 + /* FIXMEchpe: wtf is this doing? */ + + /* If we have an active screen, match its size and zoom */ + if (priv->active_screen) + { + int current_width, current_height; + double scale; + + terminal_screen_get_size (priv->active_screen, ¤t_width, ¤t_height); + vte_terminal_set_size (VTE_TERMINAL (screen), current_width, current_height); + + scale = terminal_screen_get_font_scale (priv->active_screen); + terminal_screen_set_font_scale (screen, scale); + } +#endif + + if (priv->present_on_insert) + { + gtk_window_present_with_time (GTK_WINDOW (window), gtk_get_current_event_time ()); + priv->present_on_insert = FALSE; + } +} + +static void +notebook_page_removed_callback (GtkWidget *notebook, + GtkWidget *container, + guint page_num, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalScreen *screen; + int pages; + + if (priv->disposed) + return; + + screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (container)); + + _terminal_debug_print (TERMINAL_DEBUG_MDI, + "[window %p] MDI: screen %p removed\n", + window, screen); + + g_signal_handlers_disconnect_by_func (G_OBJECT (screen), + G_CALLBACK (profile_set_callback), + window); + + g_signal_handlers_disconnect_by_func (G_OBJECT (screen), + G_CALLBACK (sync_screen_title), + window); + + g_signal_handlers_disconnect_by_func (G_OBJECT (screen), + G_CALLBACK (sync_screen_icon_title), + window); + + g_signal_handlers_disconnect_by_func (G_OBJECT (screen), + G_CALLBACK (sync_screen_icon_title_set), + window); + + g_signal_handlers_disconnect_by_func (G_OBJECT (screen), + G_CALLBACK (terminal_window_update_copy_sensitivity), + window); + + g_signal_handlers_disconnect_by_func (screen, + G_CALLBACK (screen_show_popup_menu_callback), + window); + + g_signal_handlers_disconnect_by_func (screen, + G_CALLBACK (screen_match_clicked_cb), + window); + g_signal_handlers_disconnect_by_func (screen, + G_CALLBACK (screen_resize_window_cb), + window); + + g_signal_handlers_disconnect_by_func (screen, + G_CALLBACK (screen_close_cb), + window); + + terminal_window_update_tabs_menu_sensitivity (window); + update_tab_visibility (window, 0); + terminal_window_update_search_sensitivity (screen, window); + + pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)); + if (pages == 1) + { + terminal_window_set_size (window, priv->active_screen, TRUE); + } + else if (pages == 0) + { + gtk_widget_destroy (GTK_WIDGET (window)); + } +} + +void +terminal_window_update_geometry (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkWidget *widget; + GdkGeometry hints; + int char_width; + int char_height; + + if (priv->active_screen == NULL) + return; + + widget = GTK_WIDGET (priv->active_screen); + + /* We set geometry hints from the active term; best thing + * I can think of to do. Other option would be to try to + * get some kind of union of all hints from all terms in the + * window, but that doesn't make too much sense. + */ + terminal_screen_get_cell_size (priv->active_screen, &char_width, &char_height); + + if (char_width != priv->old_char_width || + char_height != priv->old_char_height || + widget != (GtkWidget*) priv->old_geometry_widget) + { + GtkBorder *inner_border = NULL; + + /* FIXME Since we're using xthickness/ythickness to compute + * padding we need to change the hints when the theme changes. + */ + + gtk_widget_style_get (widget, "inner-border", &inner_border, NULL); + + hints.base_width = (inner_border ? (inner_border->left + inner_border->right) : 0); + hints.base_height = (inner_border ? (inner_border->top + inner_border->bottom) : 0); + + gtk_border_free (inner_border); + +#define MIN_WIDTH_CHARS 4 +#define MIN_HEIGHT_CHARS 1 + + hints.width_inc = char_width; + hints.height_inc = char_height; + + /* min size is min size of just the geometry widget, remember. */ + hints.min_width = hints.base_width + hints.width_inc * MIN_WIDTH_CHARS; + hints.min_height = hints.base_height + hints.height_inc * MIN_HEIGHT_CHARS; + + gtk_window_set_geometry_hints (GTK_WINDOW (window), + widget, + &hints, + GDK_HINT_RESIZE_INC | + GDK_HINT_MIN_SIZE | + GDK_HINT_BASE_SIZE); + + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] hints: base %dx%d min %dx%d inc %d %d\n", + window, + hints.base_width, + hints.base_height, + hints.min_width, + hints.min_height, + hints.width_inc, + hints.height_inc); + + priv->old_char_width = hints.width_inc; + priv->old_char_height = hints.height_inc; + priv->old_geometry_widget = widget; + } + else + { + _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, + "[window %p] hints: increment unchanged, not setting\n", + window); + } +} + +static void +file_new_window_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalApp *app; + TerminalWindow *new_window; + TerminalProfile *profile; + char *new_working_directory; + + app = terminal_app_get (); + + profile = g_object_get_data (G_OBJECT (action), PROFILE_DATA_KEY); + if (!profile) + profile = terminal_screen_get_profile (priv->active_screen); + if (!profile) + profile = terminal_app_get_profile_for_new_term (app); + if (!profile) + return; + + if (_terminal_profile_get_forgotten (profile)) + return; + + new_window = terminal_app_new_window (app, gtk_widget_get_screen (GTK_WIDGET (window))); + + new_working_directory = terminal_screen_get_current_dir_with_fallback (priv->active_screen); + terminal_app_new_terminal (app, new_window, profile, + NULL, NULL, + new_working_directory, + terminal_screen_get_initial_environment (priv->active_screen), + 1.0); + g_free (new_working_directory); + + gtk_window_present (GTK_WINDOW (new_window)); +} + +static void +file_new_tab_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalApp *app; + TerminalProfile *profile; + char *new_working_directory; + + app = terminal_app_get (); + profile = g_object_get_data (G_OBJECT (action), PROFILE_DATA_KEY); + if (!profile) + profile = terminal_screen_get_profile (priv->active_screen); + if (!profile) + profile = terminal_app_get_profile_for_new_term (app); + if (!profile) + return; + + if (_terminal_profile_get_forgotten (profile)) + return; + + new_working_directory = terminal_screen_get_current_dir_with_fallback (priv->active_screen); + terminal_app_new_terminal (app, window, profile, + NULL, NULL, + new_working_directory, + terminal_screen_get_initial_environment (priv->active_screen), + 1.0); + g_free (new_working_directory); +} + +static void +confirm_close_response_cb (GtkWidget *dialog, + int response, + TerminalWindow *window) +{ + TerminalScreen *screen; + + screen = g_object_get_data (G_OBJECT (dialog), "close-screen"); + + gtk_widget_destroy (dialog); + + if (response != GTK_RESPONSE_ACCEPT) + return; + + if (screen) + terminal_window_remove_screen (window, screen); + else + gtk_widget_destroy (GTK_WIDGET (window)); +} + +/* Returns: TRUE if closing needs to wait until user confirmation; + * FALSE if the terminal or window can close immediately. + */ +static gboolean +confirm_close_window_or_tab (TerminalWindow *window, + TerminalScreen *screen) +{ + TerminalWindowPrivate *priv = window->priv; + GtkWidget *dialog; + MateConfClient *client; + gboolean do_confirm; + int n_tabs; + + if (priv->confirm_close_dialog) + { + /* WTF, already have one? It's modal, so how did that happen? */ + gtk_dialog_response (GTK_DIALOG (priv->confirm_close_dialog), + GTK_RESPONSE_DELETE_EVENT); + } + + client = mateconf_client_get_default (); + do_confirm = mateconf_client_get_bool (client, CONF_GLOBAL_PREFIX "/confirm_window_close", NULL); + g_object_unref (client); + if (!do_confirm) + return FALSE; + + if (screen) + { + do_confirm = terminal_screen_has_foreground_process (screen); + n_tabs = 1; + } + else + { + GList *tabs, *t; + + do_confirm = FALSE; + + tabs = terminal_window_list_screen_containers (window); + n_tabs = g_list_length (tabs); + + for (t = tabs; t != NULL; t = t->next) + { + TerminalScreen *terminal_screen; + + terminal_screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (t->data)); + if (terminal_screen_has_foreground_process (terminal_screen)) + { + do_confirm = TRUE; + break; + } + } + g_list_free (tabs); + } + + if (!do_confirm) + return FALSE; + + dialog = priv->confirm_close_dialog = + gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_CANCEL, + "%s", n_tabs > 1 ? _("Close this window?") : _("Close this terminal?")); + + if (n_tabs > 1) + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", _("There are still processes running in some terminals in this window. " + "Closing the window will kill all of them.")); + else + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", _("There is still a process running in this terminal. " + "Closing the terminal will kill it.")); + + gtk_window_set_title (GTK_WINDOW (dialog), ""); + + gtk_dialog_add_button (GTK_DIALOG (dialog), n_tabs > 1 ? _("C_lose Window") : _("C_lose Terminal"), GTK_RESPONSE_ACCEPT); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); + + g_object_set_data (G_OBJECT (dialog), "close-screen", screen); + + g_signal_connect (dialog, "destroy", + G_CALLBACK (gtk_widget_destroyed), &priv->confirm_close_dialog); + g_signal_connect (dialog, "response", + G_CALLBACK (confirm_close_response_cb), window); + + gtk_window_present (GTK_WINDOW (dialog)); + + return TRUE; +} + +static void +file_close_window_callback (GtkAction *action, + TerminalWindow *window) +{ + if (confirm_close_window_or_tab (window, NULL)) + return; + + gtk_widget_destroy (GTK_WIDGET (window)); +} + +#ifdef ENABLE_SAVE +static void +save_contents_dialog_on_response (GtkDialog *dialog, gint response_id, gpointer terminal) +{ + GtkWindow *parent; + gchar *filename_uri = NULL; + GFile *file; + GOutputStream *stream; + GError *error = NULL; + + if (response_id != GTK_RESPONSE_ACCEPT) + { + gtk_widget_destroy (GTK_WIDGET (dialog)); + return; + } + + parent = (GtkWindow*) gtk_widget_get_ancestor (GTK_WIDGET (terminal), GTK_TYPE_WINDOW); + filename_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog)); + + gtk_widget_destroy (GTK_WIDGET (dialog)); + + if (filename_uri == NULL) + return; + + file = g_file_new_for_uri (filename_uri); + stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error)); + + if (stream) + { + /* XXX + * FIXME + * This is a sync operation. + * Should be replaced with the async version when vte implements that. + */ + vte_terminal_write_contents (terminal, stream, + VTE_TERMINAL_WRITE_DEFAULT, + NULL, &error); + g_object_unref (stream); + } + + if (error) + { + terminal_util_show_error_dialog (parent, NULL, error, + "%s", _("Could not save contents")); + g_error_free (error); + } + + g_object_unref(file); + g_free(filename_uri); +} +#endif /* ENABLE_SAVE */ + +static void +file_save_contents_callback (GtkAction *action, + TerminalWindow *window) +{ +#ifdef ENABLE_SAVE + GtkWidget *dialog = NULL; + TerminalWindowPrivate *priv = window->priv; + VteTerminal *terminal; + + if (!priv->active_screen) + return; + + terminal = VTE_TERMINAL (priv->active_screen); + g_return_if_fail (VTE_IS_TERMINAL (terminal)); + + dialog = gtk_file_chooser_dialog_new (_("Save as..."), + GTK_WINDOW(window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); + /* XXX where should we save to? */ + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)); + + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW(window)); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE); + + g_signal_connect (dialog, "response", G_CALLBACK (save_contents_dialog_on_response), terminal); + g_signal_connect (dialog, "delete_event", G_CALLBACK (terminal_util_dialog_response_on_delete), NULL); + + gtk_window_present (GTK_WINDOW (dialog)); +#endif /* ENABLE_SAVE */ +} + +static void +file_close_tab_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalScreen *active_screen = priv->active_screen; + + if (!active_screen) + return; + + if (confirm_close_window_or_tab (window, active_screen)) + return; + + terminal_window_remove_screen (window, active_screen); +} + +static void +edit_copy_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (!priv->active_screen) + return; + + vte_terminal_copy_clipboard (VTE_TERMINAL (priv->active_screen)); +} + +typedef struct { + TerminalScreen *screen; + gboolean uris_as_paths; +} PasteData; + +static void +clipboard_uris_received_cb (GtkClipboard *clipboard, + /* const */ char **uris, + PasteData *data) +{ + char *text; + gsize len; + + if (!uris) { + g_object_unref (data->screen); + g_slice_free (PasteData, data); + return; + } + + /* This potentially modifies the strings in |uris| but that's ok */ + if (data->uris_as_paths) + terminal_util_transform_uris_to_quoted_fuse_paths (uris); + + text = terminal_util_concat_uris (uris, &len); + vte_terminal_feed_child (VTE_TERMINAL (data->screen), text, len); + g_free (text); + + g_object_unref (data->screen); + g_slice_free (PasteData, data); +} + +static void +clipboard_targets_received_cb (GtkClipboard *clipboard, + GdkAtom *targets, + int n_targets, + PasteData *data) +{ + if (!targets) { + g_object_unref (data->screen); + g_slice_free (PasteData, data); + return; + } + + if (gtk_targets_include_uri (targets, n_targets)) { + gtk_clipboard_request_uris (clipboard, + (GtkClipboardURIReceivedFunc) clipboard_uris_received_cb, + data); + return; + } else /* if (gtk_targets_include_text (targets, n_targets)) */ { + vte_terminal_paste_clipboard (VTE_TERMINAL (data->screen)); + } + + g_object_unref (data->screen); + g_slice_free (PasteData, data); +} + +static void +edit_paste_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkClipboard *clipboard; + PasteData *data; + const char *name; + + if (!priv->active_screen) + return; + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD); + name = gtk_action_get_name (action); + + data = g_slice_new (PasteData); + data->screen = g_object_ref (priv->active_screen); + data->uris_as_paths = (name == I_("EditPasteURIPaths") || name == I_("PopupPasteURIPaths")); + + gtk_clipboard_request_targets (clipboard, + (GtkClipboardTargetsReceivedFunc) clipboard_targets_received_cb, + data); +} + +static void +edit_select_all_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (!priv->active_screen) + return; + + vte_terminal_select_all (VTE_TERMINAL (priv->active_screen)); +} + +static void +edit_keybindings_callback (GtkAction *action, + TerminalWindow *window) +{ + terminal_app_edit_keybindings (terminal_app_get (), + GTK_WINDOW (window)); +} + +static void +edit_current_profile_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + terminal_app_edit_profile (terminal_app_get (), + terminal_screen_get_profile (priv->active_screen), + GTK_WINDOW (window), + NULL); +} + +static void +file_new_profile_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + terminal_app_new_profile (terminal_app_get (), + terminal_screen_get_profile (priv->active_screen), + GTK_WINDOW (window)); +} + +static void +edit_profiles_callback (GtkAction *action, + TerminalWindow *window) +{ + terminal_app_manage_profiles (terminal_app_get (), + GTK_WINDOW (window)); +} + +static void +view_menubar_toggled_callback (GtkToggleAction *action, + TerminalWindow *window) +{ + terminal_window_set_menubar_visible (window, gtk_toggle_action_get_active (action)); +} + +static void +view_fullscreen_toggled_callback (GtkToggleAction *action, + TerminalWindow *window) +{ + g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (window))); + + if (gtk_toggle_action_get_active (action)) + gtk_window_fullscreen (GTK_WINDOW (window)); + else + gtk_window_unfullscreen (GTK_WINDOW (window)); +} + +static const double zoom_factors[] = { + TERMINAL_SCALE_MINIMUM, + TERMINAL_SCALE_XXXXX_SMALL, + TERMINAL_SCALE_XXXX_SMALL, + TERMINAL_SCALE_XXX_SMALL, + PANGO_SCALE_XX_SMALL, + PANGO_SCALE_X_SMALL, + PANGO_SCALE_SMALL, + PANGO_SCALE_MEDIUM, + PANGO_SCALE_LARGE, + PANGO_SCALE_X_LARGE, + PANGO_SCALE_XX_LARGE, + TERMINAL_SCALE_XXX_LARGE, + TERMINAL_SCALE_XXXX_LARGE, + TERMINAL_SCALE_XXXXX_LARGE, + TERMINAL_SCALE_MAXIMUM +}; + +static gboolean +find_larger_zoom_factor (double current, + double *found) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (zoom_factors); ++i) + { + /* Find a font that's larger than this one */ + if ((zoom_factors[i] - current) > 1e-6) + { + *found = zoom_factors[i]; + return TRUE; + } + } + + return FALSE; +} + +static gboolean +find_smaller_zoom_factor (double current, + double *found) +{ + int i; + + i = (int) G_N_ELEMENTS (zoom_factors) - 1; + while (i >= 0) + { + /* Find a font that's smaller than this one */ + if ((current - zoom_factors[i]) > 1e-6) + { + *found = zoom_factors[i]; + return TRUE; + } + + --i; + } + + return FALSE; +} + +static void +view_zoom_in_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + double current; + + if (priv->active_screen == NULL) + return; + + current = terminal_screen_get_font_scale (priv->active_screen); + if (!find_larger_zoom_factor (current, ¤t)) + return; + + terminal_screen_set_font_scale (priv->active_screen, current); + terminal_window_update_zoom_sensitivity (window); +} + +static void +view_zoom_out_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + double current; + + if (priv->active_screen == NULL) + return; + + current = terminal_screen_get_font_scale (priv->active_screen); + if (!find_smaller_zoom_factor (current, ¤t)) + return; + + terminal_screen_set_font_scale (priv->active_screen, current); + terminal_window_update_zoom_sensitivity (window); +} + +static void +view_zoom_normal_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (priv->active_screen == NULL) + return; + + terminal_screen_set_font_scale (priv->active_screen, PANGO_SCALE_MEDIUM); + terminal_window_update_zoom_sensitivity (window); +} + + +static void +search_find_response_callback (GtkWidget *dialog, + int response, + gpointer user_data) +{ + TerminalWindow *window = TERMINAL_WINDOW (user_data); + TerminalWindowPrivate *priv = window->priv; + TerminalSearchFlags flags; + GRegex *regex; + + if (response != GTK_RESPONSE_ACCEPT) + return; + + if (G_UNLIKELY (!priv->active_screen)) + return; + + regex = terminal_search_dialog_get_regex (dialog); + g_return_if_fail (regex != NULL); + + flags = terminal_search_dialog_get_search_flags (dialog); + + vte_terminal_search_set_gregex (VTE_TERMINAL (priv->active_screen), regex); + vte_terminal_search_set_wrap_around (VTE_TERMINAL (priv->active_screen), + (flags & TERMINAL_SEARCH_FLAG_WRAP_AROUND)); + + if (flags & TERMINAL_SEARCH_FLAG_BACKWARDS) + vte_terminal_search_find_previous (VTE_TERMINAL (priv->active_screen)); + else + vte_terminal_search_find_next (VTE_TERMINAL (priv->active_screen)); + + terminal_window_update_search_sensitivity (priv->active_screen, window); +} + +static gboolean +search_dialog_delete_event_cb (GtkWidget *widget, + GdkEventAny *event, + gpointer user_data) +{ + /* prevent destruction */ + return TRUE; +} + +static void +search_find_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (!priv->search_find_dialog) { + GtkWidget *dialog; + + dialog = priv->search_find_dialog = terminal_search_dialog_new (GTK_WINDOW (window)); + + g_signal_connect (dialog, "destroy", + G_CALLBACK (gtk_widget_destroyed), &priv->search_find_dialog); + g_signal_connect (dialog, "response", + G_CALLBACK (search_find_response_callback), window); + g_signal_connect (dialog, "delete-event", + G_CALLBACK (search_dialog_delete_event_cb), NULL); + } + + terminal_search_dialog_present (priv->search_find_dialog); +} + +static void +search_find_next_callback (GtkAction *action, + TerminalWindow *window) +{ + if (G_UNLIKELY (!window->priv->active_screen)) + return; + + vte_terminal_search_find_next (VTE_TERMINAL (window->priv->active_screen)); +} + +static void +search_find_prev_callback (GtkAction *action, + TerminalWindow *window) +{ + if (G_UNLIKELY (!window->priv->active_screen)) + return; + + vte_terminal_search_find_previous (VTE_TERMINAL (window->priv->active_screen)); +} + +static void +search_clear_highlight_callback (GtkAction *action, + TerminalWindow *window) +{ + if (G_UNLIKELY (!window->priv->active_screen)) + return; + + vte_terminal_search_set_gregex (VTE_TERMINAL (window->priv->active_screen), NULL); +} + +static void +terminal_set_title_dialog_response_cb (GtkWidget *dialog, + int response, + TerminalScreen *screen) +{ + if (response == GTK_RESPONSE_OK) + { + GtkEntry *entry; + const char *text; + + entry = GTK_ENTRY (g_object_get_data (G_OBJECT (dialog), "title-entry")); + text = gtk_entry_get_text (entry); + terminal_screen_set_user_title (screen, text); + } + + gtk_widget_destroy (dialog); +} + +static void +terminal_set_title_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkWidget *dialog, *message_area, *hbox, *label, *entry; + + if (priv->active_screen == NULL) + return; + + /* FIXME: hook the screen up so this dialogue closes if the terminal screen closes */ + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_OTHER, + GTK_BUTTONS_OK_CANCEL, + "%s", ""); + + gtk_window_set_title (GTK_WINDOW (dialog), _("Set Title")); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_window_set_role (GTK_WINDOW (dialog), "mate-terminal-change-title"); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + /* Alternative button order was set automatically by GtkMessageDialog */ + + g_signal_connect (dialog, "response", + G_CALLBACK (terminal_set_title_dialog_response_cb), priv->active_screen); + g_signal_connect (dialog, "delete-event", + G_CALLBACK (terminal_util_dialog_response_on_delete), NULL); + +#if GTK_CHECK_VERSION (2, 90, 6) + message_area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG (dialog)); + gtk_container_foreach (GTK_CONTAINER (message_area), (GtkCallback) gtk_widget_hide, NULL); +#else + label = GTK_MESSAGE_DIALOG (dialog)->label; + gtk_widget_hide (label); + message_area = gtk_widget_get_parent (label); +#endif + + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (message_area), hbox, FALSE, FALSE, 0); + + label = gtk_label_new_with_mnemonic (_("_Title:")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + entry = gtk_entry_new (); + gtk_entry_set_width_chars (GTK_ENTRY (entry), 32); + gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry); + gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0); + gtk_widget_show_all (hbox); + + gtk_widget_grab_focus (entry); + gtk_entry_set_text (GTK_ENTRY (entry), terminal_screen_get_raw_title (priv->active_screen)); + gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); + g_object_set_data (G_OBJECT (dialog), "title-entry", entry); + + gtk_window_present (GTK_WINDOW (dialog)); +} + +static void +terminal_add_encoding_callback (GtkAction *action, + TerminalWindow *window) +{ + terminal_app_edit_encodings (terminal_app_get (), + GTK_WINDOW (window)); +} + +static void +terminal_reset_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (priv->active_screen == NULL) + return; + + vte_terminal_reset (VTE_TERMINAL (priv->active_screen), TRUE, FALSE); +} + +static void +terminal_reset_clear_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + if (priv->active_screen == NULL) + return; + + vte_terminal_reset (VTE_TERMINAL (priv->active_screen), TRUE, TRUE); +} + +static void +tabs_next_or_previous_tab_cb (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkNotebookClass *klass; + GtkBindingSet *binding_set; + const char *name; + guint keyval = 0; + + name = gtk_action_get_name (action); + if (strcmp (name, "TabsNext") == 0) { + keyval = GDK_Page_Down; + } else if (strcmp (name, "TabsPrevious") == 0) { + keyval = GDK_Page_Up; + } + + klass = GTK_NOTEBOOK_GET_CLASS (GTK_NOTEBOOK (priv->notebook)); + binding_set = gtk_binding_set_by_class (klass); + gtk_binding_set_activate (gtk_binding_set_by_class (klass), + keyval, + GDK_CONTROL_MASK, + GTK_OBJECT (priv->notebook)); +} + +static void +tabs_move_left_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkNotebook *notebook = GTK_NOTEBOOK (priv->notebook); + gint page_num,last_page; + GtkWidget *page; + + page_num = gtk_notebook_get_current_page (notebook); + last_page = gtk_notebook_get_n_pages (notebook) - 1; + page = gtk_notebook_get_nth_page (notebook, page_num); + + gtk_notebook_reorder_child (notebook, page, page_num == 0 ? last_page : page_num - 1); +} + +static void +tabs_move_right_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + GtkNotebook *notebook = GTK_NOTEBOOK (priv->notebook); + gint page_num,last_page; + GtkWidget *page; + + page_num = gtk_notebook_get_current_page (notebook); + last_page = gtk_notebook_get_n_pages (notebook) - 1; + page = gtk_notebook_get_nth_page (notebook, page_num); + + gtk_notebook_reorder_child (notebook, page, page_num == last_page ? 0 : page_num + 1); +} + +static void +tabs_detach_tab_callback (GtkAction *action, + TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + TerminalApp *app; + TerminalWindow *new_window; + TerminalScreen *screen; + char *geometry; + int width, height; + + app = terminal_app_get (); + + screen = priv->active_screen; + + /* FIXME: this seems wrong if tabs are shown in the window */ + terminal_screen_get_size (screen, &width, &height); + geometry = g_strdup_printf ("%dx%d", width, height); + + new_window = terminal_app_new_window (app, gtk_widget_get_screen (GTK_WIDGET (window))); + + terminal_window_move_screen (window, new_window, screen, -1); + + gtk_window_parse_geometry (GTK_WINDOW (new_window), geometry); + g_free (geometry); + + gtk_window_present_with_time (GTK_WINDOW (new_window), gtk_get_current_event_time ()); +} + +static void +help_contents_callback (GtkAction *action, + TerminalWindow *window) +{ + terminal_util_show_help (NULL, GTK_WINDOW (window)); +} + +#define ABOUT_GROUP "About" +#define EMAILIFY(string) (g_strdelimit ((string), "%", '@')) + +static void +help_about_callback (GtkAction *action, + TerminalWindow *window) +{ + static const char copyright[] = + "Copyright © 2002–2004 Havoc Pennington\n" + "Copyright © 2003–2004, 2007 Mariano Suárez-Alvarez\n" + "Copyright © 2006 Guilherme de S. Pastore\n" + "Copyright © 2007–2010 Christian Persch"; + char *licence_text; + GKeyFile *key_file; + GError *error = NULL; + char **authors, **contributors, **artists, **documenters, **array_strv; + gsize n_authors = 0, n_contributors = 0, n_artists = 0, n_documenters = 0 , i; + GPtrArray *array; + + key_file = g_key_file_new (); + if (!g_key_file_load_from_file (key_file, TERM_PKGDATADIR G_DIR_SEPARATOR_S "terminal.about", 0, &error)) + { + g_warning ("Couldn't load about data: %s\n", error->message); + g_error_free (error); + g_key_file_free (key_file); + return; + } + + authors = g_key_file_get_string_list (key_file, ABOUT_GROUP, "Authors", &n_authors, NULL); + contributors = g_key_file_get_string_list (key_file, ABOUT_GROUP, "Contributors", &n_contributors, NULL); + artists = g_key_file_get_string_list (key_file, ABOUT_GROUP, "Artists", &n_artists, NULL); + documenters = g_key_file_get_string_list (key_file, ABOUT_GROUP, "Documenters", &n_documenters, NULL); + g_key_file_free (key_file); + + array = g_ptr_array_new (); + + for (i = 0; i < n_authors; ++i) + g_ptr_array_add (array, EMAILIFY (authors[i])); + g_free (authors); /* strings are now owned by the array */ + + if (n_contributors > 0) + { + g_ptr_array_add (array, g_strdup ("")); + g_ptr_array_add (array, g_strdup (_("Contributors:"))); + for (i = 0; i < n_contributors; ++i) + g_ptr_array_add (array, EMAILIFY (contributors[i])); + } + g_free (contributors); /* strings are now owned by the array */ + + g_ptr_array_add (array, NULL); + array_strv = (char **) g_ptr_array_free (array, FALSE); + + for (i = 0; i < n_artists; ++i) + artists[i] = EMAILIFY (artists[i]); + for (i = 0; i < n_documenters; ++i) + documenters[i] = EMAILIFY (documenters[i]); + + licence_text = terminal_util_get_licence_text (); + + gtk_show_about_dialog (GTK_WINDOW (window), + "program-name", _("MATE Terminal"), + "copyright", copyright, + "comments", _("A terminal emulator for the MATE desktop"), + "version", VERSION, + "authors", array_strv, + "artists", artists, + "documenters", documenters, + "license", licence_text, + "wrap-license", TRUE, + "translator-credits", _("translator-credits"), + "logo-icon-name", MATE_TERMINAL_ICON_NAME, + NULL); + + g_strfreev (array_strv); + g_strfreev (artists); + g_strfreev (documenters); + g_free (licence_text); +} + +GtkUIManager * +terminal_window_get_ui_manager (TerminalWindow *window) +{ + TerminalWindowPrivate *priv = window->priv; + + return priv->ui_manager; +} + +void +terminal_window_save_state (TerminalWindow *window, + GKeyFile *key_file, + const char *group) +{ + TerminalWindowPrivate *priv = window->priv; + GList *tabs, *lt; + TerminalScreen *active_screen; + GdkWindowState state; + GPtrArray *tab_names_array; + char **tab_names; + gsize len; + + //XXXif (priv->menub)//XXX + g_key_file_set_boolean (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_MENUBAR_VISIBLE, + priv->menubar_visible); + + g_key_file_set_string (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_ROLE, + gtk_window_get_role (GTK_WINDOW (window))); + + state = gdk_window_get_state (gtk_widget_get_window (GTK_WIDGET (window))); + if (state & GDK_WINDOW_STATE_MAXIMIZED) + g_key_file_set_boolean (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_MAXIMIZED, TRUE); + if (state & GDK_WINDOW_STATE_FULLSCREEN) + g_key_file_set_boolean (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_FULLSCREEN, TRUE); + + active_screen = terminal_window_get_active (window); + tabs = terminal_window_list_screen_containers (window); + + tab_names_array = g_ptr_array_sized_new (g_list_length (tabs) + 1); + + for (lt = tabs; lt != NULL; lt = lt->next) + { + TerminalScreen *screen; + char *tab_group; + + screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (lt->data)); + + tab_group = g_strdup_printf ("Terminal%p", screen); + g_ptr_array_add (tab_names_array, tab_group); + + terminal_screen_save_config (screen, key_file, tab_group); + + if (screen == active_screen) + { + int w, h, x, y; + char *geometry; + + g_key_file_set_string (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_ACTIVE_TAB, tab_group); + + /* FIXME saving the geometry is not great :-/ */ + terminal_screen_get_size (screen, &w, &h); + gtk_window_get_position (GTK_WINDOW (window), &x, &y); + geometry = g_strdup_printf ("%dx%d+%d+%d", w, h, x, y); + g_key_file_set_string (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_GEOMETRY, geometry); + g_free (geometry); + } + } + + g_list_free (tabs); + + len = tab_names_array->len; + g_ptr_array_add (tab_names_array, NULL); + tab_names = (char **) g_ptr_array_free (tab_names_array, FALSE); + g_key_file_set_string_list (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_TABS, (const char * const *) tab_names, len); + g_strfreev (tab_names); +} diff --git a/src/terminal-window.h b/src/terminal-window.h new file mode 100644 index 0000000..b1a84b0 --- /dev/null +++ b/src/terminal-window.h @@ -0,0 +1,106 @@ +/* + * Copyright © 2001 Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TERMINAL_WINDOW_H +#define TERMINAL_WINDOW_H + +#include <gtk/gtk.h> + +#include "terminal-screen.h" + +G_BEGIN_DECLS + +#define TERMINAL_TYPE_WINDOW (terminal_window_get_type ()) +#define TERMINAL_WINDOW(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TERMINAL_TYPE_WINDOW, TerminalWindow)) +#define TERMINAL_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TERMINAL_TYPE_WINDOW, TerminalWindowClass)) +#define TERMINAL_IS_WINDOW(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TERMINAL_TYPE_WINDOW)) +#define TERMINAL_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TERMINAL_TYPE_WINDOW)) +#define TERMINAL_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TERMINAL_TYPE_WINDOW, TerminalWindowClass)) + +typedef struct _TerminalWindowClass TerminalWindowClass; +typedef struct _TerminalWindowPrivate TerminalWindowPrivate; + +struct _TerminalWindow +{ + GtkWindow parent_instance; + + TerminalWindowPrivate *priv; +}; + +struct _TerminalWindowClass +{ + GtkWindowClass parent_class; + +}; + +GType terminal_window_get_type (void) G_GNUC_CONST; + +TerminalWindow* terminal_window_new (void); + +void terminal_window_set_is_restored (TerminalWindow *window); + +GtkUIManager *terminal_window_get_ui_manager (TerminalWindow *window); + +void terminal_window_add_screen (TerminalWindow *window, + TerminalScreen *screen, + int position); + +void terminal_window_remove_screen (TerminalWindow *window, + TerminalScreen *screen); + +void terminal_window_move_screen (TerminalWindow *source_window, + TerminalWindow *dest_window, + TerminalScreen *screen, + int dest_position); + +/* Menubar visibility is part of session state, except that + * if it isn't restored from session, the window gets the setting + * from the profile of the first screen added to the window + */ +void terminal_window_set_menubar_visible (TerminalWindow *window, + gboolean setting); +gboolean terminal_window_get_menubar_visible (TerminalWindow *window); + +void terminal_window_switch_screen (TerminalWindow *window, + TerminalScreen *screen); +TerminalScreen* terminal_window_get_active (TerminalWindow *window); + +GList* terminal_window_list_screen_containers (TerminalWindow *window); + +void terminal_window_update_geometry (TerminalWindow *window); +void terminal_window_set_size (TerminalWindow *window, + TerminalScreen *screen, + gboolean even_if_mapped); +void terminal_window_set_size_force_grid (TerminalWindow *window, + TerminalScreen *screen, + gboolean even_if_mapped, + int force_grid_width, + int force_grid_height); + +GtkWidget* terminal_window_get_notebook (TerminalWindow *window); + +gboolean terminal_window_uses_argb_visual (TerminalWindow *window); + +void terminal_window_save_state (TerminalWindow *window, + GKeyFile *key_file, + const char *group); + +G_END_DECLS + +#endif /* TERMINAL_WINDOW_H */ diff --git a/src/terminal.about b/src/terminal.about new file mode 100644 index 0000000..1923fd9 --- /dev/null +++ b/src/terminal.about @@ -0,0 +1,5 @@ +[About] +Authors=Behdad Esfahbod <behdad%mate.org>;Guilherme de S. Pastore <gpastore%mate.org>;Havoc Pennington <hp%redhat.com>;Christian Persch <chpe%mate.org>;Mariano Suárez-Alvarez <mariano%mate.org>; +Contributors= +;Artists= +Documenters=MATE Documentation Team <mate-doc-list%mate.org>; diff --git a/src/terminal.c b/src/terminal.c new file mode 100644 index 0000000..03a619e --- /dev/null +++ b/src/terminal.c @@ -0,0 +1,619 @@ +/* + * Copyright © 2001, 2002 Havoc Pennington + * Copyright © 2002 Red Hat, Inc. + * Copyright © 2002 Sun Microsystems + * Copyright © 2003 Mariano Suarez-Alvarez + * Copyright © 2008, 2010 Christian Persch + * + * Mate-terminal 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 3 of the License, or + * (at your option) any later version. + * + * Mate-terminal 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, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <errno.h> +#include <locale.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gstdio.h> +#include <gio/gio.h> + +#include <gdk/gdkx.h> + +#ifdef WITH_SMCLIENT +#include "eggsmclient.h" +#endif + +#include "terminal-accels.h" +#include "terminal-app.h" +#include "terminal-debug.h" +#include "terminal-intl.h" +#include "terminal-options.h" +#include "terminal-util.h" + +#define TERMINAL_FACTORY_SERVICE_NAME_PREFIX "org.mate.Terminal.Display" +#define TERMINAL_FACTORY_SERVICE_PATH "/org/mate/Terminal/Factory" +#define TERMINAL_FACTORY_INTERFACE_NAME "org.mate.Terminal.Factory" + +static char * +ay_to_string (GVariant *variant, + GError **error) +{ + gsize len; + const char *data; + + data = g_variant_get_fixed_array (variant, &len, sizeof (char)); + if (len == 0) + return NULL; + + /* Make sure there are no embedded NULs */ + if (memchr (data, '\0', len) != NULL) { + g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "String is shorter than claimed"); + return NULL; + } + + return g_strndup (data, len); +} + +static char ** +ay_to_strv (GVariant *variant, + int *argc) +{ + GPtrArray *argv; + const char *data, *nullbyte; + gsize data_len; + gssize len; + + data = g_variant_get_fixed_array (variant, &data_len, sizeof (char)); + if (data_len == 0 || data_len > G_MAXSSIZE) { + *argc = 0; + return NULL; + } + + argv = g_ptr_array_new (); + + len = data_len; + do { + gssize string_len; + + nullbyte = memchr (data, '\0', len); + + string_len = nullbyte ? (gssize) (nullbyte - data) : len; + g_ptr_array_add (argv, g_strndup (data, string_len)); + + len -= string_len + 1; + data += string_len + 1; + } while (len > 0); + + if (argc) + *argc = argv->len; + + /* NULL terminate */ + g_ptr_array_add (argv, NULL); + return (char **) g_ptr_array_free (argv, FALSE); +} + +static GVariant * +string_to_ay (const char *string) +{ + gsize len; + char *data; + + len = strlen (string); + data = g_strndup (string, len); + + return g_variant_new_from_data (G_VARIANT_TYPE ("ay"), data, len, TRUE, g_free, data); +} + +typedef struct { + char *factory_name; + TerminalOptions *options; + int exit_code; + char **argv; + int argc; +} OwnData; + +static void +method_call_cb (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + if (g_strcmp0 (method_name, "HandleArguments") == 0) { + TerminalOptions *options = NULL; + GVariant *v_wd, *v_display, *v_sid, *v_envv, *v_argv; + char *working_directory = NULL, *display_name = NULL, *startup_id = NULL; + char **envv = NULL, **argv = NULL; + int argc; + GError *error = NULL; + + g_variant_get (parameters, "(@ay@ay@ay@ay@ay)", + &v_wd, &v_display, &v_sid, &v_envv, &v_argv); + + working_directory = ay_to_string (v_wd, &error); + if (error) + goto out; + display_name = ay_to_string (v_display, &error); + if (error) + goto out; + startup_id = ay_to_string (v_sid, &error); + if (error) + goto out; + envv = ay_to_strv (v_envv, NULL); + argv = ay_to_strv (v_argv, &argc); + + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, + "Factory invoked with working-dir='%s' display='%s' startup-id='%s'\n", + working_directory ? working_directory : "(null)", + display_name ? display_name : "(null)", + startup_id ? startup_id : "(null)"); + + options = terminal_options_parse (working_directory, + display_name, + startup_id, + envv, + TRUE, + TRUE, + &argc, &argv, + &error, + NULL); + + if (options != NULL) { + terminal_app_handle_options (terminal_app_get (), options, FALSE /* no resume */, &error); + terminal_options_free (options); + } + + out: + g_variant_unref (v_wd); + g_free (working_directory); + g_variant_unref (v_display); + g_free (display_name); + g_variant_unref (v_sid); + g_free (startup_id); + g_variant_unref (v_envv); + g_strfreev (envv); + g_variant_unref (v_argv); + g_strfreev (argv); + + if (error == NULL) { + g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); + } else { + g_dbus_method_invocation_return_gerror (invocation, error); + g_error_free (error); + } + } +} + +static void +bus_acquired_cb (GDBusConnection *connection, + const char *name, + gpointer user_data) +{ + static const char dbus_introspection_xml[] = + "<node name='/org/mate/Terminal'>" + "<interface name='org.mate.Terminal.Factory'>" + "<method name='HandleArguments'>" + "<arg type='ay' name='working_directory' direction='in' />" + "<arg type='ay' name='display_name' direction='in' />" + "<arg type='ay' name='startup_id' direction='in' />" + "<arg type='ay' name='environment' direction='in' />" + "<arg type='ay' name='arguments' direction='in' />" + "</method>" + "</interface>" + "</node>"; + + static const GDBusInterfaceVTable interface_vtable = { + method_call_cb, + NULL, + NULL, + }; + + OwnData *data = (OwnData *) user_data; + GDBusNodeInfo *introspection_data; + guint registration_id; + GError *error = NULL; + + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, + "Bus %s acquired\n", name); + + introspection_data = g_dbus_node_info_new_for_xml (dbus_introspection_xml, NULL); + g_assert (introspection_data != NULL); + + registration_id = g_dbus_connection_register_object (connection, + TERMINAL_FACTORY_SERVICE_PATH, + introspection_data->interfaces[0], + &interface_vtable, + NULL, NULL, + &error); + g_dbus_node_info_unref (introspection_data); + + if (registration_id == 0) { + g_printerr ("Failed to register object: %s\n", error->message); + g_error_free (error); + data->exit_code = EXIT_FAILURE; + gtk_main_quit (); + } +} + +static void +name_acquired_cb (GDBusConnection *connection, + const char *name, + gpointer user_data) +{ + OwnData *data = (OwnData *) user_data; + GError *error = NULL; + + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, + "Acquired the name %s on the session bus\n", name); + + if (data->options == NULL) { + /* Name re-acquired!? */ + g_assert_not_reached (); + } + + + if (!terminal_app_handle_options (terminal_app_get (), data->options, TRUE /* do resume */, &error)) { + g_printerr ("Failed to handle options: %s\n", error->message); + g_error_free (error); + data->exit_code = EXIT_FAILURE; + gtk_main_quit (); + } + + terminal_options_free (data->options); + data->options = NULL; +} + +static void +name_lost_cb (GDBusConnection *connection, + const char *name, + gpointer user_data) +{ + OwnData *data = (OwnData *) user_data; + GError *error = NULL; + char **envv; + int envc, i; + GVariantBuilder builder; + GVariant *value; + GString *string; + char *s; + gsize len; + + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, + "Lost the name %s on the session bus\n", name); + + /* Couldn't get the connection? No way to continue! */ + if (connection == NULL) { + data->exit_code = EXIT_FAILURE; + gtk_main_quit (); + return; + } + + if (data->options == NULL) { + /* Already handled */ + data->exit_code = EXIT_SUCCESS; + gtk_main_quit (); + return; + } + + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, + "Forwarding arguments to existing instance\n"); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ayayayayay)")); + + g_variant_builder_add (&builder, "@ay", string_to_ay (data->options->default_working_dir)); + g_variant_builder_add (&builder, "@ay", string_to_ay (data->options->display_name)); + g_variant_builder_add (&builder, "@ay", string_to_ay (data->options->startup_id)); + + string = g_string_new (NULL); + envv = g_listenv (); + envc = g_strv_length (envv); + for (i = 0; i < envc; ++i) + { + const char *value; + + value = g_getenv (envv[i]); + if (value == NULL) + continue; + + if (i > 0) + g_string_append_c (string, '\0'); + + g_string_append_printf (string, "%s=%s", envv[i], value); + } + + len = string->len; + s = g_string_free (string, FALSE); + g_variant_builder_add (&builder, "@ay", + g_variant_new_from_data (G_VARIANT_TYPE ("ay"), s, len, TRUE, g_free, s)); + + string = g_string_new (NULL); + + for (i = 0; i < data->argc; ++i) + { + if (i > 0) + g_string_append_c (string, '\0'); + g_string_append (string, data->argv[i]); + } + + len = string->len; + s = g_string_free (string, FALSE); + g_variant_builder_add (&builder, "@ay", + g_variant_new_from_data (G_VARIANT_TYPE ("ay"), s, len, TRUE, g_free, s)); + + value = g_dbus_connection_call_sync (connection, + data->factory_name, + TERMINAL_FACTORY_SERVICE_PATH, + TERMINAL_FACTORY_INTERFACE_NAME, + "HandleArguments", + g_variant_builder_end (&builder), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (value == NULL) { + g_printerr ("Failed to forward arguments: %s\n", error->message); + g_error_free (error); + data->exit_code = EXIT_FAILURE; + gtk_main_quit (); + } else { + g_variant_unref (value); + data->exit_code = EXIT_SUCCESS; + } + + terminal_options_free (data->options); + data->options = NULL; + + gtk_main_quit (); +} + +/* Settings storage works as follows: + * /apps/mate-terminal/global/ + * /apps/mate-terminal/profiles/Foo/ + * + * It's somewhat tricky to manage the profiles/ dir since we need to track + * the list of profiles, but mateconf doesn't have a concept of notifying that + * a directory has appeared or disappeared. + * + * Session state is stored entirely in the RestartCommand command line. + * + * The number one rule: all stored information is EITHER per-session, + * per-profile, or set from a command line option. THERE CAN BE NO + * OVERLAP. The UI and implementation totally break if you overlap + * these categories. See mate-terminal 1.x for why. + * + * Don't use this code as an example of how to use MateConf - it's hugely + * overcomplicated due to the profiles stuff. Most apps should not + * have to do scary things of this nature, and should not have + * a profiles feature. + * + */ + +/* Copied from libcaja/caja-program-choosing.c; Needed in case + * we have no DESKTOP_STARTUP_ID (with its accompanying timestamp). + */ +static Time +slowly_and_stupidly_obtain_timestamp (Display *xdisplay) +{ + Window xwindow; + XEvent event; + + { + XSetWindowAttributes attrs; + Atom atom_name; + Atom atom_type; + const char *name; + + attrs.override_redirect = True; + attrs.event_mask = PropertyChangeMask | StructureNotifyMask; + + xwindow = + XCreateWindow (xdisplay, + RootWindow (xdisplay, 0), + -100, -100, 1, 1, + 0, + CopyFromParent, + CopyFromParent, + (Visual *)CopyFromParent, + CWOverrideRedirect | CWEventMask, + &attrs); + + atom_name = XInternAtom (xdisplay, "WM_NAME", TRUE); + g_assert (atom_name != None); + atom_type = XInternAtom (xdisplay, "STRING", TRUE); + g_assert (atom_type != None); + + name = "Fake Window"; + XChangeProperty (xdisplay, + xwindow, atom_name, + atom_type, + 8, PropModeReplace, (unsigned char *)name, strlen (name)); + } + + XWindowEvent (xdisplay, + xwindow, + PropertyChangeMask, + &event); + + XDestroyWindow(xdisplay, xwindow); + + return event.xproperty.time; +} + +static char * +get_factory_name_for_display (const char *display_name) +{ + GString *name; + const char *p; + + name = g_string_sized_new (strlen (TERMINAL_FACTORY_SERVICE_NAME_PREFIX) + strlen (display_name) + 1 /* NUL */); + g_string_append (name, TERMINAL_FACTORY_SERVICE_NAME_PREFIX); + + for (p = display_name; *p; ++p) + { + if (g_ascii_isalnum (*p)) + g_string_append_c (name, *p); + else + g_string_append_c (name, '_'); + } + + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, + "Factory name is \"%s\"\n", name->str); + + return g_string_free (name, FALSE); +} + +int +main (int argc, char **argv) +{ + int i; + char **argv_copy; + int argc_copy; + const char *startup_id, *display_name, *home_dir; + GdkDisplay *display; + TerminalOptions *options; + GError *error = NULL; + char *working_directory; + int ret = EXIT_SUCCESS; + + setlocale (LC_ALL, ""); + + bindtextdomain (GETTEXT_PACKAGE, TERM_LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + /* MateConf uses MateCORBA2 which need GThread. See bug #565516 */ + g_thread_init (NULL); + + _terminal_debug_init (); + + /* Make a NULL-terminated copy since we may need it later */ + argv_copy = g_new (char *, argc + 1); + for (i = 0; i < argc; ++i) + argv_copy [i] = argv [i]; + argv_copy [i] = NULL; + argc_copy = argc; + + startup_id = g_getenv ("DESKTOP_STARTUP_ID"); + + working_directory = g_get_current_dir (); + + /* Now change directory to $HOME so we don't prevent unmounting, e.g. if the + * factory is started by caja-open-terminal. See bug #565328. + * On failure back to /. + */ + home_dir = g_get_home_dir (); + if (home_dir == NULL || chdir (home_dir) < 0) + (void) chdir ("/"); + + options = terminal_options_parse (working_directory, + NULL, + startup_id, + NULL, + FALSE, + FALSE, + &argc, &argv, + &error, + gtk_get_option_group (TRUE), +#ifdef WITH_SMCLIENT + egg_sm_client_get_option_group (), +#endif + NULL); + + g_free (working_directory); + + if (options == NULL) { + g_printerr (_("Failed to parse arguments: %s\n"), error->message); + g_error_free (error); + exit (EXIT_FAILURE); + } + + g_set_application_name (_("Terminal")); + + /* Unset the these env variables, so they doesn't end up + * in the factory's env and thus in the terminals' envs. + */ + g_unsetenv ("DESKTOP_STARTUP_ID"); + g_unsetenv ("GIO_LAUNCHED_DESKTOP_FILE_PID"); + g_unsetenv ("GIO_LAUNCHED_DESKTOP_FILE"); + + /* Do this here so that gdk_display is initialized */ + if (options->startup_id == NULL) + { + /* Create a fake one containing a timestamp that we can use */ + Time timestamp; + + timestamp = slowly_and_stupidly_obtain_timestamp (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); + + options->startup_id = g_strdup_printf ("_TIME%lu", timestamp); + } + + display = gdk_display_get_default (); + display_name = gdk_display_get_name (display); + options->display_name = g_strdup (display_name); + + if (options->use_factory) { + OwnData *data; + guint owner_id; + + data = g_new (OwnData, 1); + data->factory_name = get_factory_name_for_display (display_name); + data->options = options; + data->exit_code = -1; + data->argv = argv_copy; + data->argc = argc_copy; + + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + data->factory_name, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_cb, + name_acquired_cb, + name_lost_cb, + data, NULL); + + gtk_main (); + + ret = data->exit_code; + g_bus_unown_name (owner_id); + + g_free (data->factory_name); + g_free (data); + + } else { + + terminal_app_handle_options (terminal_app_get (), options, TRUE /* allow resume */, &error); + terminal_options_free (options); + + if (error == NULL) { + gtk_main (); + } else { + g_printerr ("Error handling options: %s\n", error->message); + g_error_free (error); + ret = EXIT_FAILURE; + } + } + + terminal_app_shutdown (); + + g_free (argv_copy); + + return ret; +} diff --git a/src/terminal.xml b/src/terminal.xml new file mode 100644 index 0000000..3c9cbed --- /dev/null +++ b/src/terminal.xml @@ -0,0 +1,116 @@ +<ui> + <menubar> + <menu action="File"> + <menuitem action="FileNewWindow" /> + <menu action="FileNewWindowProfiles" /> + <menuitem action="FileNewTab" /> + <menu action="FileNewTabProfiles" /> + <separator /> + <menuitem action="FileNewProfile" /> + <menuitem action="FileSaveContents" /> + <separator /> + <menuitem action="FileCloseTab" /> + <menuitem action="FileCloseWindow" /> + </menu> + <menu action="Edit"> + <menuitem action="EditCopy" /> + <menuitem action="EditPaste" /> + <menuitem action="EditPasteURIPaths" /> + <separator /> + <menuitem action="EditSelectAll" /> + <separator /> + <menuitem action="EditProfiles" /> + <menuitem action="EditKeybindings" /> + <menuitem action="EditCurrentProfile" /> + </menu> + <menu action="View"> + <menuitem action="ViewMenubar" /> + <menuitem action="ViewFullscreen" /> + <separator /> + <menuitem action="ViewZoomIn" /> + <menuitem action="ViewZoomOut" /> + <menuitem action="ViewZoom100" /> + </menu> + <menu action="Search"> + <menuitem action="SearchFind"/> + <menuitem action="SearchFindNext"/> + <menuitem action="SearchFindPrevious"/> + <!-- + <menuitem action="SearchIncrementalSearch"/> + --> + <!-- + <separator/> + <menuitem name="SearchClearHighlight" action="SearchClearHighlight"/> + --> + <!-- + <separator/> + <menuitem name="SearchGoToLineMenu" action="SearchGoToLine"/> + --> + </menu> + <menu action="Terminal"> + <menu action="TerminalProfiles" /> + <menuitem action="TerminalSetTitle" /> + <menu action="TerminalSetEncoding" > + <placeholder name="EncodingsPH" /> + <separator /> + <menuitem action="TerminalAddEncoding" /> + </menu> + <separator /> + <menuitem action="TerminalReset" /> + <menuitem action="TerminalResetClear" /> + <separator /> + <placeholder name="TerminalSizeToPH" /> + </menu> + <menu action="Tabs"> + <menuitem action="TabsNext" /> + <menuitem action="TabsPrevious" /> + <separator /> + <menuitem action="TabsMoveLeft" /> + <menuitem action="TabsMoveRight" /> + <separator /> + <menuitem action="TabsDetach" /> + <separator /> + </menu> + <menu action="Help"> + <menuitem action="HelpContents" /> + <menuitem action="HelpAbout" /> + </menu> + </menubar> + + <popup name="Popup" action="Popup"> + <menuitem action="PopupSendEmail" /> + <menuitem action="PopupCopyEmailAddress" /> + <menuitem action="PopupCall" /> + <menuitem action="PopupCopyCallAddress" /> + <menuitem action="PopupOpenLink" /> + <menuitem action="PopupCopyLinkAddress" /> + <separator /> + <menuitem action="PopupNewTerminal" /> + <menuitem action="PopupNewTab" /> + <separator /> + <menuitem action="PopupCloseTab" /> + <menuitem action="PopupCloseWindow" /> + <separator /> + <menuitem action="PopupCopy" /> + <menuitem action="PopupPaste" /> + <menuitem action="PopupPasteURIPaths" /> + <separator /> + <menu action="PopupTerminalProfiles"> + <placeholder name="ProfilesPH" /> + <separator /> + <menuitem action="EditCurrentProfile" /> + </menu> + <menuitem action="ViewMenubar" /> + <menuitem action="PopupLeaveFullscreen" /> + <separator /> + <menuitem action="PopupInputMethods" /> + </popup> + <popup name="NotebookPopup" action="NotebookPopup"> + <menuitem action="TabsMoveLeft"/> + <menuitem action="TabsMoveRight"/> + <separator /> + <menuitem action="TerminalSetTitle" /> + <separator /> + <menuitem action="FileCloseTab"/> + </popup> +</ui> |