summaryrefslogtreecommitdiffstats
path: root/app/gui
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--app/gui/Makefile.am80
-rw-r--r--app/gui/Makefile.in1018
-rw-r--r--app/gui/dbus-service.xml31
-rw-r--r--app/gui/gimpdbusservice-generated.c1721
-rw-r--r--app/gui/gimpdbusservice-generated.h282
-rw-r--r--app/gui/gimpdbusservice.c457
-rw-r--r--app/gui/gimpdbusservice.h69
-rw-r--r--app/gui/gimpuiconfigurer.c622
-rw-r--r--app/gui/gimpuiconfigurer.h57
-rw-r--r--app/gui/gui-message.c501
-rw-r--r--app/gui/gui-message.h29
-rw-r--r--app/gui/gui-types.h27
-rw-r--r--app/gui/gui-unique.c442
-rw-r--r--app/gui/gui-unique.h31
-rw-r--r--app/gui/gui-vtable.c934
-rw-r--r--app/gui/gui-vtable.h31
-rw-r--r--app/gui/gui.c1077
-rw-r--r--app/gui/gui.h30
-rw-r--r--app/gui/icon-themes.c250
-rw-r--r--app/gui/icon-themes.h34
-rw-r--r--app/gui/session.c486
-rw-r--r--app/gui/session.h35
-rw-r--r--app/gui/splash.c736
-rw-r--r--app/gui/splash.h32
-rw-r--r--app/gui/themes.c535
-rw-r--r--app/gui/themes.h34
26 files changed, 9581 insertions, 0 deletions
diff --git a/app/gui/Makefile.am b/app/gui/Makefile.am
new file mode 100644
index 0000000..579cd63
--- /dev/null
+++ b/app/gui/Makefile.am
@@ -0,0 +1,80 @@
+## Process this file with automake to produce Makefile.in
+
+if PLATFORM_OSX
+xobjective_c = "-xobjective-c"
+xobjective_cxx = "-xobjective-c++"
+xnone = "-xnone"
+endif
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-GUI\" \
+ -DGIMP_COMMAND=\"@GIMP_COMMAND@\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(GIO_UNIX_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(GTK_MAC_INTEGRATION_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(xnone)
+
+noinst_LIBRARIES = libappgui.a
+
+libappgui_a_sources = \
+ gimpdbusservice.c \
+ gimpdbusservice.h \
+ gimpuiconfigurer.c \
+ gimpuiconfigurer.h \
+ gui.c \
+ gui.h \
+ gui-message.c \
+ gui-message.h \
+ gui-unique.c \
+ gui-unique.h \
+ gui-vtable.c \
+ gui-vtable.h \
+ gui-types.h \
+ icon-themes.c \
+ icon-themes.h \
+ session.c \
+ session.h \
+ splash.c \
+ splash.h \
+ themes.c \
+ themes.h
+
+libappgui_a_built_sources = \
+ gimpdbusservice-generated.c \
+ gimpdbusservice-generated.h
+
+libappgui_a_SOURCES = $(libappgui_a_built_sources) $(libappgui_a_sources)
+
+BUILT_SOURCES = $(libappgui_a_built_sources)
+
+EXTRA_DIST = \
+ dbus-service.xml
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = $(libappgui_a_built_sources)
+CLEANFILES = $(gen_sources)
+
+$(srcdir)/gimpdbusservice.c: $(libappgui_a_built_sources)
+
+$(libappgui_a_built_sources): $(srcdir)/dbus-service.xml
+ $(GDBUS_CODEGEN) --interface-prefix org.gimp.GIMP. \
+ --generate-c-code gimpdbusservice-generated \
+ --c-namespace GimpDBusService \
+ $(srcdir)/dbus-service.xml
diff --git a/app/gui/Makefile.in b/app/gui/Makefile.in
new file mode 100644
index 0000000..5bc3164
--- /dev/null
+++ b/app/gui/Makefile.in
@@ -0,0 +1,1018 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = app/gui
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo " AR " $@;
+am__v_AR_1 =
+libappgui_a_AR = $(AR) $(ARFLAGS)
+libappgui_a_LIBADD =
+am__objects_1 = gimpdbusservice-generated.$(OBJEXT)
+am__objects_2 = gimpdbusservice.$(OBJEXT) gimpuiconfigurer.$(OBJEXT) \
+ gui.$(OBJEXT) gui-message.$(OBJEXT) gui-unique.$(OBJEXT) \
+ gui-vtable.$(OBJEXT) icon-themes.$(OBJEXT) session.$(OBJEXT) \
+ splash.$(OBJEXT) themes.$(OBJEXT)
+am_libappgui_a_OBJECTS = $(am__objects_1) $(am__objects_2)
+libappgui_a_OBJECTS = $(am_libappgui_a_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimpdbusservice-generated.Po \
+ ./$(DEPDIR)/gimpdbusservice.Po ./$(DEPDIR)/gimpuiconfigurer.Po \
+ ./$(DEPDIR)/gui-message.Po ./$(DEPDIR)/gui-unique.Po \
+ ./$(DEPDIR)/gui-vtable.Po ./$(DEPDIR)/gui.Po \
+ ./$(DEPDIR)/icon-themes.Po ./$(DEPDIR)/session.Po \
+ ./$(DEPDIR)/splash.Po ./$(DEPDIR)/themes.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+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_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+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_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libappgui_a_SOURCES)
+DIST_SOURCES = $(libappgui_a_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_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_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+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@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+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@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+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@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+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@
+@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c"
+@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++"
+@PLATFORM_OSX_TRUE@xnone = "-xnone"
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-GUI\" \
+ -DGIMP_COMMAND=\"@GIMP_COMMAND@\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(GIO_UNIX_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(GTK_MAC_INTEGRATION_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(xnone)
+
+noinst_LIBRARIES = libappgui.a
+libappgui_a_sources = \
+ gimpdbusservice.c \
+ gimpdbusservice.h \
+ gimpuiconfigurer.c \
+ gimpuiconfigurer.h \
+ gui.c \
+ gui.h \
+ gui-message.c \
+ gui-message.h \
+ gui-unique.c \
+ gui-unique.h \
+ gui-vtable.c \
+ gui-vtable.h \
+ gui-types.h \
+ icon-themes.c \
+ icon-themes.h \
+ session.c \
+ session.h \
+ splash.c \
+ splash.h \
+ themes.c \
+ themes.h
+
+libappgui_a_built_sources = \
+ gimpdbusservice-generated.c \
+ gimpdbusservice-generated.h
+
+libappgui_a_SOURCES = $(libappgui_a_built_sources) $(libappgui_a_sources)
+BUILT_SOURCES = $(libappgui_a_built_sources)
+EXTRA_DIST = \
+ dbus-service.xml
+
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = $(libappgui_a_built_sources)
+CLEANFILES = $(gen_sources)
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu app/gui/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu app/gui/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__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ 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-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libappgui.a: $(libappgui_a_OBJECTS) $(libappgui_a_DEPENDENCIES) $(EXTRA_libappgui_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libappgui.a
+ $(AM_V_AR)$(libappgui_a_AR) libappgui.a $(libappgui_a_OBJECTS) $(libappgui_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libappgui.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdbusservice-generated.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdbusservice.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpuiconfigurer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui-message.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui-unique.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui-vtable.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icon-themes.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/splash.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/themes.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.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
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.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
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(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
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ 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-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ 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"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) 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:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimpdbusservice-generated.Po
+ -rm -f ./$(DEPDIR)/gimpdbusservice.Po
+ -rm -f ./$(DEPDIR)/gimpuiconfigurer.Po
+ -rm -f ./$(DEPDIR)/gui-message.Po
+ -rm -f ./$(DEPDIR)/gui-unique.Po
+ -rm -f ./$(DEPDIR)/gui-vtable.Po
+ -rm -f ./$(DEPDIR)/gui.Po
+ -rm -f ./$(DEPDIR)/icon-themes.Po
+ -rm -f ./$(DEPDIR)/session.Po
+ -rm -f ./$(DEPDIR)/splash.Po
+ -rm -f ./$(DEPDIR)/themes.Po
+ -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 -f ./$(DEPDIR)/gimpdbusservice-generated.Po
+ -rm -f ./$(DEPDIR)/gimpdbusservice.Po
+ -rm -f ./$(DEPDIR)/gimpuiconfigurer.Po
+ -rm -f ./$(DEPDIR)/gui-message.Po
+ -rm -f ./$(DEPDIR)/gui-unique.Po
+ -rm -f ./$(DEPDIR)/gui-vtable.Po
+ -rm -f ./$(DEPDIR)/gui.Po
+ -rm -f ./$(DEPDIR)/icon-themes.Po
+ -rm -f ./$(DEPDIR)/session.Po
+ -rm -f ./$(DEPDIR)/splash.Po
+ -rm -f ./$(DEPDIR)/themes.Po
+ -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: all check install install-am install-exec install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLIBRARIES \
+ cscopelist-am ctags ctags-am 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 tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+$(srcdir)/gimpdbusservice.c: $(libappgui_a_built_sources)
+
+$(libappgui_a_built_sources): $(srcdir)/dbus-service.xml
+ $(GDBUS_CODEGEN) --interface-prefix org.gimp.GIMP. \
+ --generate-c-code gimpdbusservice-generated \
+ --c-namespace GimpDBusService \
+ $(srcdir)/dbus-service.xml
+
+# 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/app/gui/dbus-service.xml b/app/gui/dbus-service.xml
new file mode 100644
index 0000000..5badd9b
--- /dev/null
+++ b/app/gui/dbus-service.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node>
+
+ <interface name="org.gimp.GIMP.UI">
+
+ <method name="Open">
+ <arg type="s" name="uri" direction="in" />
+ <arg type="b" name="success" direction="out" />
+ </method>
+
+ <method name="OpenAsNew">
+ <arg type="s" name="uri" direction="in" />
+ <arg type="b" name="success" direction="out" />
+ </method>
+
+ <method name="BatchRun">
+ <arg type="s" name="interpreter" direction="in" />
+ <arg type="s" name="command" direction="in" />
+ <arg type="b" name="success" direction="out" />
+ </method>
+
+ <method name="Activate" />
+
+ <signal name="Opened">
+ <arg type="s" name="uri" />
+ </signal>
+
+ </interface>
+
+</node>
diff --git a/app/gui/gimpdbusservice-generated.c b/app/gui/gimpdbusservice-generated.c
new file mode 100644
index 0000000..9ede0fd
--- /dev/null
+++ b/app/gui/gimpdbusservice-generated.c
@@ -0,0 +1,1721 @@
+/*
+ * This file is generated by gdbus-codegen, do not modify it.
+ *
+ * The license of this code is the same as for the D-Bus interface description
+ * it was derived from. Note that it links to GLib, so must comply with the
+ * LGPL linking clauses.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gimpdbusservice-generated.h"
+
+#include <string.h>
+#ifdef G_OS_UNIX
+# include <gio/gunixfdlist.h>
+#endif
+
+typedef struct
+{
+ GDBusArgInfo parent_struct;
+ gboolean use_gvariant;
+} _ExtendedGDBusArgInfo;
+
+typedef struct
+{
+ GDBusMethodInfo parent_struct;
+ const gchar *signal_name;
+ gboolean pass_fdlist;
+} _ExtendedGDBusMethodInfo;
+
+typedef struct
+{
+ GDBusSignalInfo parent_struct;
+ const gchar *signal_name;
+} _ExtendedGDBusSignalInfo;
+
+typedef struct
+{
+ GDBusPropertyInfo parent_struct;
+ const gchar *hyphen_name;
+ guint use_gvariant : 1;
+ guint emits_changed_signal : 1;
+} _ExtendedGDBusPropertyInfo;
+
+typedef struct
+{
+ GDBusInterfaceInfo parent_struct;
+ const gchar *hyphen_name;
+} _ExtendedGDBusInterfaceInfo;
+
+typedef struct
+{
+ const _ExtendedGDBusPropertyInfo *info;
+ guint prop_id;
+ GValue orig_value; /* the value before the change */
+} ChangedProperty;
+
+static void
+_changed_property_free (ChangedProperty *data)
+{
+ g_value_unset (&data->orig_value);
+ g_free (data);
+}
+
+static gboolean
+_g_strv_equal0 (gchar **a, gchar **b)
+{
+ gboolean ret = FALSE;
+ guint n;
+ if (a == NULL && b == NULL)
+ {
+ ret = TRUE;
+ goto out;
+ }
+ if (a == NULL || b == NULL)
+ goto out;
+ if (g_strv_length (a) != g_strv_length (b))
+ goto out;
+ for (n = 0; a[n] != NULL; n++)
+ if (g_strcmp0 (a[n], b[n]) != 0)
+ goto out;
+ ret = TRUE;
+out:
+ return ret;
+}
+
+static gboolean
+_g_variant_equal0 (GVariant *a, GVariant *b)
+{
+ gboolean ret = FALSE;
+ if (a == NULL && b == NULL)
+ {
+ ret = TRUE;
+ goto out;
+ }
+ if (a == NULL || b == NULL)
+ goto out;
+ ret = g_variant_equal (a, b);
+out:
+ return ret;
+}
+
+G_GNUC_UNUSED static gboolean
+_g_value_equal (const GValue *a, const GValue *b)
+{
+ gboolean ret = FALSE;
+ g_assert (G_VALUE_TYPE (a) == G_VALUE_TYPE (b));
+ switch (G_VALUE_TYPE (a))
+ {
+ case G_TYPE_BOOLEAN:
+ ret = (g_value_get_boolean (a) == g_value_get_boolean (b));
+ break;
+ case G_TYPE_UCHAR:
+ ret = (g_value_get_uchar (a) == g_value_get_uchar (b));
+ break;
+ case G_TYPE_INT:
+ ret = (g_value_get_int (a) == g_value_get_int (b));
+ break;
+ case G_TYPE_UINT:
+ ret = (g_value_get_uint (a) == g_value_get_uint (b));
+ break;
+ case G_TYPE_INT64:
+ ret = (g_value_get_int64 (a) == g_value_get_int64 (b));
+ break;
+ case G_TYPE_UINT64:
+ ret = (g_value_get_uint64 (a) == g_value_get_uint64 (b));
+ break;
+ case G_TYPE_DOUBLE:
+ {
+ /* Avoid -Wfloat-equal warnings by doing a direct bit compare */
+ gdouble da = g_value_get_double (a);
+ gdouble db = g_value_get_double (b);
+ ret = memcmp (&da, &db, sizeof (gdouble)) == 0;
+ }
+ break;
+ case G_TYPE_STRING:
+ ret = (g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0);
+ break;
+ case G_TYPE_VARIANT:
+ ret = _g_variant_equal0 (g_value_get_variant (a), g_value_get_variant (b));
+ break;
+ default:
+ if (G_VALUE_TYPE (a) == G_TYPE_STRV)
+ ret = _g_strv_equal0 (g_value_get_boxed (a), g_value_get_boxed (b));
+ else
+ g_critical ("_g_value_equal() does not handle type %s", g_type_name (G_VALUE_TYPE (a)));
+ break;
+ }
+ return ret;
+}
+
+/* ------------------------------------------------------------------------
+ * Code for interface org.gimp.GIMP.UI
+ * ------------------------------------------------------------------------
+ */
+
+/**
+ * SECTION:GimpDBusServiceUI
+ * @title: GimpDBusServiceUI
+ * @short_description: Generated C code for the org.gimp.GIMP.UI D-Bus interface
+ *
+ * This section contains code for working with the <link linkend="gdbus-interface-org-gimp-GIMP-UI.top_of_page">org.gimp.GIMP.UI</link> D-Bus interface in C.
+ */
+
+/* ---- Introspection data for org.gimp.GIMP.UI ---- */
+
+static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_open_IN_ARG_uri =
+{
+ {
+ -1,
+ (gchar *) "uri",
+ (gchar *) "s",
+ NULL
+ },
+ FALSE
+};
+
+static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_open_IN_ARG_pointers[] =
+{
+ &_gimp_dbus_service_ui_method_info_open_IN_ARG_uri.parent_struct,
+ NULL
+};
+
+static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_open_OUT_ARG_success =
+{
+ {
+ -1,
+ (gchar *) "success",
+ (gchar *) "b",
+ NULL
+ },
+ FALSE
+};
+
+static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_open_OUT_ARG_pointers[] =
+{
+ &_gimp_dbus_service_ui_method_info_open_OUT_ARG_success.parent_struct,
+ NULL
+};
+
+static const _ExtendedGDBusMethodInfo _gimp_dbus_service_ui_method_info_open =
+{
+ {
+ -1,
+ (gchar *) "Open",
+ (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_open_IN_ARG_pointers,
+ (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_open_OUT_ARG_pointers,
+ NULL
+ },
+ "handle-open",
+ FALSE
+};
+
+static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_open_as_new_IN_ARG_uri =
+{
+ {
+ -1,
+ (gchar *) "uri",
+ (gchar *) "s",
+ NULL
+ },
+ FALSE
+};
+
+static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_open_as_new_IN_ARG_pointers[] =
+{
+ &_gimp_dbus_service_ui_method_info_open_as_new_IN_ARG_uri.parent_struct,
+ NULL
+};
+
+static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_open_as_new_OUT_ARG_success =
+{
+ {
+ -1,
+ (gchar *) "success",
+ (gchar *) "b",
+ NULL
+ },
+ FALSE
+};
+
+static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_open_as_new_OUT_ARG_pointers[] =
+{
+ &_gimp_dbus_service_ui_method_info_open_as_new_OUT_ARG_success.parent_struct,
+ NULL
+};
+
+static const _ExtendedGDBusMethodInfo _gimp_dbus_service_ui_method_info_open_as_new =
+{
+ {
+ -1,
+ (gchar *) "OpenAsNew",
+ (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_open_as_new_IN_ARG_pointers,
+ (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_open_as_new_OUT_ARG_pointers,
+ NULL
+ },
+ "handle-open-as-new",
+ FALSE
+};
+
+static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_batch_run_IN_ARG_interpreter =
+{
+ {
+ -1,
+ (gchar *) "interpreter",
+ (gchar *) "s",
+ NULL
+ },
+ FALSE
+};
+
+static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_batch_run_IN_ARG_command =
+{
+ {
+ -1,
+ (gchar *) "command",
+ (gchar *) "s",
+ NULL
+ },
+ FALSE
+};
+
+static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_batch_run_IN_ARG_pointers[] =
+{
+ &_gimp_dbus_service_ui_method_info_batch_run_IN_ARG_interpreter.parent_struct,
+ &_gimp_dbus_service_ui_method_info_batch_run_IN_ARG_command.parent_struct,
+ NULL
+};
+
+static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_method_info_batch_run_OUT_ARG_success =
+{
+ {
+ -1,
+ (gchar *) "success",
+ (gchar *) "b",
+ NULL
+ },
+ FALSE
+};
+
+static const GDBusArgInfo * const _gimp_dbus_service_ui_method_info_batch_run_OUT_ARG_pointers[] =
+{
+ &_gimp_dbus_service_ui_method_info_batch_run_OUT_ARG_success.parent_struct,
+ NULL
+};
+
+static const _ExtendedGDBusMethodInfo _gimp_dbus_service_ui_method_info_batch_run =
+{
+ {
+ -1,
+ (gchar *) "BatchRun",
+ (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_batch_run_IN_ARG_pointers,
+ (GDBusArgInfo **) &_gimp_dbus_service_ui_method_info_batch_run_OUT_ARG_pointers,
+ NULL
+ },
+ "handle-batch-run",
+ FALSE
+};
+
+static const _ExtendedGDBusMethodInfo _gimp_dbus_service_ui_method_info_activate =
+{
+ {
+ -1,
+ (gchar *) "Activate",
+ NULL,
+ NULL,
+ NULL
+ },
+ "handle-activate",
+ FALSE
+};
+
+static const GDBusMethodInfo * const _gimp_dbus_service_ui_method_info_pointers[] =
+{
+ &_gimp_dbus_service_ui_method_info_open.parent_struct,
+ &_gimp_dbus_service_ui_method_info_open_as_new.parent_struct,
+ &_gimp_dbus_service_ui_method_info_batch_run.parent_struct,
+ &_gimp_dbus_service_ui_method_info_activate.parent_struct,
+ NULL
+};
+
+static const _ExtendedGDBusArgInfo _gimp_dbus_service_ui_signal_info_opened_ARG_uri =
+{
+ {
+ -1,
+ (gchar *) "uri",
+ (gchar *) "s",
+ NULL
+ },
+ FALSE
+};
+
+static const GDBusArgInfo * const _gimp_dbus_service_ui_signal_info_opened_ARG_pointers[] =
+{
+ &_gimp_dbus_service_ui_signal_info_opened_ARG_uri.parent_struct,
+ NULL
+};
+
+static const _ExtendedGDBusSignalInfo _gimp_dbus_service_ui_signal_info_opened =
+{
+ {
+ -1,
+ (gchar *) "Opened",
+ (GDBusArgInfo **) &_gimp_dbus_service_ui_signal_info_opened_ARG_pointers,
+ NULL
+ },
+ "opened"
+};
+
+static const GDBusSignalInfo * const _gimp_dbus_service_ui_signal_info_pointers[] =
+{
+ &_gimp_dbus_service_ui_signal_info_opened.parent_struct,
+ NULL
+};
+
+static const _ExtendedGDBusInterfaceInfo _gimp_dbus_service_ui_interface_info =
+{
+ {
+ -1,
+ (gchar *) "org.gimp.GIMP.UI",
+ (GDBusMethodInfo **) &_gimp_dbus_service_ui_method_info_pointers,
+ (GDBusSignalInfo **) &_gimp_dbus_service_ui_signal_info_pointers,
+ NULL,
+ NULL
+ },
+ "ui",
+};
+
+
+/**
+ * gimp_dbus_service_ui_interface_info:
+ *
+ * Gets a machine-readable description of the <link linkend="gdbus-interface-org-gimp-GIMP-UI.top_of_page">org.gimp.GIMP.UI</link> D-Bus interface.
+ *
+ * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free.
+ */
+GDBusInterfaceInfo *
+gimp_dbus_service_ui_interface_info (void)
+{
+ return (GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct;
+}
+
+/**
+ * gimp_dbus_service_ui_override_properties:
+ * @klass: The class structure for a #GObject derived class.
+ * @property_id_begin: The property id to assign to the first overridden property.
+ *
+ * Overrides all #GObject properties in the #GimpDBusServiceUI interface for a concrete class.
+ * The properties are overridden in the order they are defined.
+ *
+ * Returns: The last property id.
+ */
+guint
+gimp_dbus_service_ui_override_properties (GObjectClass *klass, guint property_id_begin)
+{
+ return property_id_begin - 1;
+}
+
+
+
+/**
+ * GimpDBusServiceUI:
+ *
+ * Abstract interface type for the D-Bus interface <link linkend="gdbus-interface-org-gimp-GIMP-UI.top_of_page">org.gimp.GIMP.UI</link>.
+ */
+
+/**
+ * GimpDBusServiceUIIface:
+ * @parent_iface: The parent interface.
+ * @handle_activate: Handler for the #GimpDBusServiceUI::handle-activate signal.
+ * @handle_batch_run: Handler for the #GimpDBusServiceUI::handle-batch-run signal.
+ * @handle_open: Handler for the #GimpDBusServiceUI::handle-open signal.
+ * @handle_open_as_new: Handler for the #GimpDBusServiceUI::handle-open-as-new signal.
+ * @opened: Handler for the #GimpDBusServiceUI::opened signal.
+ *
+ * Virtual table for the D-Bus interface <link linkend="gdbus-interface-org-gimp-GIMP-UI.top_of_page">org.gimp.GIMP.UI</link>.
+ */
+
+typedef GimpDBusServiceUIIface GimpDBusServiceUIInterface;
+G_DEFINE_INTERFACE (GimpDBusServiceUI, gimp_dbus_service_ui, G_TYPE_OBJECT)
+
+static void
+gimp_dbus_service_ui_default_init (GimpDBusServiceUIIface *iface)
+{
+ /* GObject signals for incoming D-Bus method calls: */
+ /**
+ * GimpDBusServiceUI::handle-open:
+ * @object: A #GimpDBusServiceUI.
+ * @invocation: A #GDBusMethodInvocation.
+ * @arg_uri: Argument passed by remote caller.
+ *
+ * Signal emitted when a remote caller is invoking the <link linkend="gdbus-method-org-gimp-GIMP-UI.Open">Open()</link> D-Bus method.
+ *
+ * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call gimp_dbus_service_ui_complete_open() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned.
+ *
+ * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run.
+ */
+ g_signal_new ("handle-open",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpDBusServiceUIIface, handle_open),
+ g_signal_accumulator_true_handled,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_BOOLEAN,
+ 2,
+ G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING);
+
+ /**
+ * GimpDBusServiceUI::handle-open-as-new:
+ * @object: A #GimpDBusServiceUI.
+ * @invocation: A #GDBusMethodInvocation.
+ * @arg_uri: Argument passed by remote caller.
+ *
+ * Signal emitted when a remote caller is invoking the <link linkend="gdbus-method-org-gimp-GIMP-UI.OpenAsNew">OpenAsNew()</link> D-Bus method.
+ *
+ * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call gimp_dbus_service_ui_complete_open_as_new() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned.
+ *
+ * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run.
+ */
+ g_signal_new ("handle-open-as-new",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpDBusServiceUIIface, handle_open_as_new),
+ g_signal_accumulator_true_handled,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_BOOLEAN,
+ 2,
+ G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING);
+
+ /**
+ * GimpDBusServiceUI::handle-batch-run:
+ * @object: A #GimpDBusServiceUI.
+ * @invocation: A #GDBusMethodInvocation.
+ * @arg_interpreter: Argument passed by remote caller.
+ * @arg_command: Argument passed by remote caller.
+ *
+ * Signal emitted when a remote caller is invoking the <link linkend="gdbus-method-org-gimp-GIMP-UI.BatchRun">BatchRun()</link> D-Bus method.
+ *
+ * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call gimp_dbus_service_ui_complete_batch_run() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned.
+ *
+ * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run.
+ */
+ g_signal_new ("handle-batch-run",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpDBusServiceUIIface, handle_batch_run),
+ g_signal_accumulator_true_handled,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_BOOLEAN,
+ 3,
+ G_TYPE_DBUS_METHOD_INVOCATION, G_TYPE_STRING, G_TYPE_STRING);
+
+ /**
+ * GimpDBusServiceUI::handle-activate:
+ * @object: A #GimpDBusServiceUI.
+ * @invocation: A #GDBusMethodInvocation.
+ *
+ * Signal emitted when a remote caller is invoking the <link linkend="gdbus-method-org-gimp-GIMP-UI.Activate">Activate()</link> D-Bus method.
+ *
+ * If a signal handler returns %TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call gimp_dbus_service_ui_complete_activate() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %G_DBUS_ERROR_UNKNOWN_METHOD error is returned.
+ *
+ * Returns: %TRUE if the invocation was handled, %FALSE to let other signal handlers run.
+ */
+ g_signal_new ("handle-activate",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpDBusServiceUIIface, handle_activate),
+ g_signal_accumulator_true_handled,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_BOOLEAN,
+ 1,
+ G_TYPE_DBUS_METHOD_INVOCATION);
+
+ /* GObject signals for received D-Bus signals: */
+ /**
+ * GimpDBusServiceUI::opened:
+ * @object: A #GimpDBusServiceUI.
+ * @arg_uri: Argument.
+ *
+ * On the client-side, this signal is emitted whenever the D-Bus signal <link linkend="gdbus-signal-org-gimp-GIMP-UI.Opened">"Opened"</link> is received.
+ *
+ * On the service-side, this signal can be used with e.g. g_signal_emit_by_name() to make the object emit the D-Bus signal.
+ */
+ g_signal_new ("opened",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpDBusServiceUIIface, opened),
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+
+}
+
+/**
+ * gimp_dbus_service_ui_emit_opened:
+ * @object: A #GimpDBusServiceUI.
+ * @arg_uri: Argument to pass with the signal.
+ *
+ * Emits the <link linkend="gdbus-signal-org-gimp-GIMP-UI.Opened">"Opened"</link> D-Bus signal.
+ */
+void
+gimp_dbus_service_ui_emit_opened (
+ GimpDBusServiceUI *object,
+ const gchar *arg_uri)
+{
+ g_signal_emit_by_name (object, "opened", arg_uri);
+}
+
+/**
+ * gimp_dbus_service_ui_call_open:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @arg_uri: Argument to pass with the method invocation.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously invokes the <link linkend="gdbus-method-org-gimp-GIMP-UI.Open">Open()</link> D-Bus method on @proxy.
+ * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()).
+ * You can then call gimp_dbus_service_ui_call_open_finish() to get the result of the operation.
+ *
+ * See gimp_dbus_service_ui_call_open_sync() for the synchronous, blocking version of this method.
+ */
+void
+gimp_dbus_service_ui_call_open (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_uri,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_dbus_proxy_call (G_DBUS_PROXY (proxy),
+ "Open",
+ g_variant_new ("(s)",
+ arg_uri),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * gimp_dbus_service_ui_call_open_finish:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_call_open().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with gimp_dbus_service_ui_call_open().
+ *
+ * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean
+gimp_dbus_service_ui_call_open_finish (
+ GimpDBusServiceUI *proxy,
+ gboolean *out_success,
+ GAsyncResult *res,
+ GError **error)
+{
+ GVariant *_ret;
+ _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error);
+ if (_ret == NULL)
+ goto _out;
+ g_variant_get (_ret,
+ "(b)",
+ out_success);
+ g_variant_unref (_ret);
+_out:
+ return _ret != NULL;
+}
+
+/**
+ * gimp_dbus_service_ui_call_open_sync:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @arg_uri: Argument to pass with the method invocation.
+ * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously invokes the <link linkend="gdbus-method-org-gimp-GIMP-UI.Open">Open()</link> D-Bus method on @proxy. The calling thread is blocked until a reply is received.
+ *
+ * See gimp_dbus_service_ui_call_open() for the asynchronous version of this method.
+ *
+ * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean
+gimp_dbus_service_ui_call_open_sync (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_uri,
+ gboolean *out_success,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GVariant *_ret;
+ _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
+ "Open",
+ g_variant_new ("(s)",
+ arg_uri),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cancellable,
+ error);
+ if (_ret == NULL)
+ goto _out;
+ g_variant_get (_ret,
+ "(b)",
+ out_success);
+ g_variant_unref (_ret);
+_out:
+ return _ret != NULL;
+}
+
+/**
+ * gimp_dbus_service_ui_call_open_as_new:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @arg_uri: Argument to pass with the method invocation.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously invokes the <link linkend="gdbus-method-org-gimp-GIMP-UI.OpenAsNew">OpenAsNew()</link> D-Bus method on @proxy.
+ * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()).
+ * You can then call gimp_dbus_service_ui_call_open_as_new_finish() to get the result of the operation.
+ *
+ * See gimp_dbus_service_ui_call_open_as_new_sync() for the synchronous, blocking version of this method.
+ */
+void
+gimp_dbus_service_ui_call_open_as_new (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_uri,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_dbus_proxy_call (G_DBUS_PROXY (proxy),
+ "OpenAsNew",
+ g_variant_new ("(s)",
+ arg_uri),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * gimp_dbus_service_ui_call_open_as_new_finish:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_call_open_as_new().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with gimp_dbus_service_ui_call_open_as_new().
+ *
+ * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean
+gimp_dbus_service_ui_call_open_as_new_finish (
+ GimpDBusServiceUI *proxy,
+ gboolean *out_success,
+ GAsyncResult *res,
+ GError **error)
+{
+ GVariant *_ret;
+ _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error);
+ if (_ret == NULL)
+ goto _out;
+ g_variant_get (_ret,
+ "(b)",
+ out_success);
+ g_variant_unref (_ret);
+_out:
+ return _ret != NULL;
+}
+
+/**
+ * gimp_dbus_service_ui_call_open_as_new_sync:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @arg_uri: Argument to pass with the method invocation.
+ * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously invokes the <link linkend="gdbus-method-org-gimp-GIMP-UI.OpenAsNew">OpenAsNew()</link> D-Bus method on @proxy. The calling thread is blocked until a reply is received.
+ *
+ * See gimp_dbus_service_ui_call_open_as_new() for the asynchronous version of this method.
+ *
+ * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean
+gimp_dbus_service_ui_call_open_as_new_sync (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_uri,
+ gboolean *out_success,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GVariant *_ret;
+ _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
+ "OpenAsNew",
+ g_variant_new ("(s)",
+ arg_uri),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cancellable,
+ error);
+ if (_ret == NULL)
+ goto _out;
+ g_variant_get (_ret,
+ "(b)",
+ out_success);
+ g_variant_unref (_ret);
+_out:
+ return _ret != NULL;
+}
+
+/**
+ * gimp_dbus_service_ui_call_batch_run:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @arg_interpreter: Argument to pass with the method invocation.
+ * @arg_command: Argument to pass with the method invocation.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously invokes the <link linkend="gdbus-method-org-gimp-GIMP-UI.BatchRun">BatchRun()</link> D-Bus method on @proxy.
+ * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()).
+ * You can then call gimp_dbus_service_ui_call_batch_run_finish() to get the result of the operation.
+ *
+ * See gimp_dbus_service_ui_call_batch_run_sync() for the synchronous, blocking version of this method.
+ */
+void
+gimp_dbus_service_ui_call_batch_run (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_interpreter,
+ const gchar *arg_command,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_dbus_proxy_call (G_DBUS_PROXY (proxy),
+ "BatchRun",
+ g_variant_new ("(ss)",
+ arg_interpreter,
+ arg_command),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * gimp_dbus_service_ui_call_batch_run_finish:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_call_batch_run().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with gimp_dbus_service_ui_call_batch_run().
+ *
+ * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean
+gimp_dbus_service_ui_call_batch_run_finish (
+ GimpDBusServiceUI *proxy,
+ gboolean *out_success,
+ GAsyncResult *res,
+ GError **error)
+{
+ GVariant *_ret;
+ _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error);
+ if (_ret == NULL)
+ goto _out;
+ g_variant_get (_ret,
+ "(b)",
+ out_success);
+ g_variant_unref (_ret);
+_out:
+ return _ret != NULL;
+}
+
+/**
+ * gimp_dbus_service_ui_call_batch_run_sync:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @arg_interpreter: Argument to pass with the method invocation.
+ * @arg_command: Argument to pass with the method invocation.
+ * @out_success: (out) (optional): Return location for return parameter or %NULL to ignore.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously invokes the <link linkend="gdbus-method-org-gimp-GIMP-UI.BatchRun">BatchRun()</link> D-Bus method on @proxy. The calling thread is blocked until a reply is received.
+ *
+ * See gimp_dbus_service_ui_call_batch_run() for the asynchronous version of this method.
+ *
+ * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean
+gimp_dbus_service_ui_call_batch_run_sync (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_interpreter,
+ const gchar *arg_command,
+ gboolean *out_success,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GVariant *_ret;
+ _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
+ "BatchRun",
+ g_variant_new ("(ss)",
+ arg_interpreter,
+ arg_command),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cancellable,
+ error);
+ if (_ret == NULL)
+ goto _out;
+ g_variant_get (_ret,
+ "(b)",
+ out_success);
+ g_variant_unref (_ret);
+_out:
+ return _ret != NULL;
+}
+
+/**
+ * gimp_dbus_service_ui_call_activate:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously invokes the <link linkend="gdbus-method-org-gimp-GIMP-UI.Activate">Activate()</link> D-Bus method on @proxy.
+ * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()).
+ * You can then call gimp_dbus_service_ui_call_activate_finish() to get the result of the operation.
+ *
+ * See gimp_dbus_service_ui_call_activate_sync() for the synchronous, blocking version of this method.
+ */
+void
+gimp_dbus_service_ui_call_activate (
+ GimpDBusServiceUI *proxy,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_dbus_proxy_call (G_DBUS_PROXY (proxy),
+ "Activate",
+ g_variant_new ("()"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * gimp_dbus_service_ui_call_activate_finish:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_call_activate().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with gimp_dbus_service_ui_call_activate().
+ *
+ * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean
+gimp_dbus_service_ui_call_activate_finish (
+ GimpDBusServiceUI *proxy,
+ GAsyncResult *res,
+ GError **error)
+{
+ GVariant *_ret;
+ _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error);
+ if (_ret == NULL)
+ goto _out;
+ g_variant_get (_ret,
+ "()");
+ g_variant_unref (_ret);
+_out:
+ return _ret != NULL;
+}
+
+/**
+ * gimp_dbus_service_ui_call_activate_sync:
+ * @proxy: A #GimpDBusServiceUIProxy.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously invokes the <link linkend="gdbus-method-org-gimp-GIMP-UI.Activate">Activate()</link> D-Bus method on @proxy. The calling thread is blocked until a reply is received.
+ *
+ * See gimp_dbus_service_ui_call_activate() for the asynchronous version of this method.
+ *
+ * Returns: (skip): %TRUE if the call succeeded, %FALSE if @error is set.
+ */
+gboolean
+gimp_dbus_service_ui_call_activate_sync (
+ GimpDBusServiceUI *proxy,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GVariant *_ret;
+ _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),
+ "Activate",
+ g_variant_new ("()"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cancellable,
+ error);
+ if (_ret == NULL)
+ goto _out;
+ g_variant_get (_ret,
+ "()");
+ g_variant_unref (_ret);
+_out:
+ return _ret != NULL;
+}
+
+/**
+ * gimp_dbus_service_ui_complete_open:
+ * @object: A #GimpDBusServiceUI.
+ * @invocation: (transfer full): A #GDBusMethodInvocation.
+ * @success: Parameter to return.
+ *
+ * Helper function used in service implementations to finish handling invocations of the <link linkend="gdbus-method-org-gimp-GIMP-UI.Open">Open()</link> D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar.
+ *
+ * This method will free @invocation, you cannot use it afterwards.
+ */
+void
+gimp_dbus_service_ui_complete_open (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation,
+ gboolean success)
+{
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(b)",
+ success));
+}
+
+/**
+ * gimp_dbus_service_ui_complete_open_as_new:
+ * @object: A #GimpDBusServiceUI.
+ * @invocation: (transfer full): A #GDBusMethodInvocation.
+ * @success: Parameter to return.
+ *
+ * Helper function used in service implementations to finish handling invocations of the <link linkend="gdbus-method-org-gimp-GIMP-UI.OpenAsNew">OpenAsNew()</link> D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar.
+ *
+ * This method will free @invocation, you cannot use it afterwards.
+ */
+void
+gimp_dbus_service_ui_complete_open_as_new (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation,
+ gboolean success)
+{
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(b)",
+ success));
+}
+
+/**
+ * gimp_dbus_service_ui_complete_batch_run:
+ * @object: A #GimpDBusServiceUI.
+ * @invocation: (transfer full): A #GDBusMethodInvocation.
+ * @success: Parameter to return.
+ *
+ * Helper function used in service implementations to finish handling invocations of the <link linkend="gdbus-method-org-gimp-GIMP-UI.BatchRun">BatchRun()</link> D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar.
+ *
+ * This method will free @invocation, you cannot use it afterwards.
+ */
+void
+gimp_dbus_service_ui_complete_batch_run (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation,
+ gboolean success)
+{
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(b)",
+ success));
+}
+
+/**
+ * gimp_dbus_service_ui_complete_activate:
+ * @object: A #GimpDBusServiceUI.
+ * @invocation: (transfer full): A #GDBusMethodInvocation.
+ *
+ * Helper function used in service implementations to finish handling invocations of the <link linkend="gdbus-method-org-gimp-GIMP-UI.Activate">Activate()</link> D-Bus method. If you instead want to finish handling an invocation by returning an error, use g_dbus_method_invocation_return_error() or similar.
+ *
+ * This method will free @invocation, you cannot use it afterwards.
+ */
+void
+gimp_dbus_service_ui_complete_activate (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation)
+{
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("()"));
+}
+
+/* ------------------------------------------------------------------------ */
+
+/**
+ * GimpDBusServiceUIProxy:
+ *
+ * The #GimpDBusServiceUIProxy structure contains only private data and should only be accessed using the provided API.
+ */
+
+/**
+ * GimpDBusServiceUIProxyClass:
+ * @parent_class: The parent class.
+ *
+ * Class structure for #GimpDBusServiceUIProxy.
+ */
+
+struct _GimpDBusServiceUIProxyPrivate
+{
+ GData *qdata;
+};
+
+static void gimp_dbus_service_ui_proxy_iface_init (GimpDBusServiceUIIface *iface);
+
+#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38
+G_DEFINE_TYPE_WITH_CODE (GimpDBusServiceUIProxy, gimp_dbus_service_ui_proxy, G_TYPE_DBUS_PROXY,
+ G_ADD_PRIVATE (GimpDBusServiceUIProxy)
+ G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI, gimp_dbus_service_ui_proxy_iface_init))
+
+#else
+G_DEFINE_TYPE_WITH_CODE (GimpDBusServiceUIProxy, gimp_dbus_service_ui_proxy, G_TYPE_DBUS_PROXY,
+ G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI, gimp_dbus_service_ui_proxy_iface_init))
+
+#endif
+static void
+gimp_dbus_service_ui_proxy_finalize (GObject *object)
+{
+ GimpDBusServiceUIProxy *proxy = GIMP_DBUS_SERVICE_UI_PROXY (object);
+ g_datalist_clear (&proxy->priv->qdata);
+ G_OBJECT_CLASS (gimp_dbus_service_ui_proxy_parent_class)->finalize (object);
+}
+
+static void
+gimp_dbus_service_ui_proxy_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec G_GNUC_UNUSED)
+{
+}
+
+static void
+gimp_dbus_service_ui_proxy_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec G_GNUC_UNUSED)
+{
+}
+
+static void
+gimp_dbus_service_ui_proxy_g_signal (GDBusProxy *proxy,
+ const gchar *sender_name G_GNUC_UNUSED,
+ const gchar *signal_name,
+ GVariant *parameters)
+{
+ _ExtendedGDBusSignalInfo *info;
+ GVariantIter iter;
+ GVariant *child;
+ GValue *paramv;
+ gsize num_params;
+ gsize n;
+ guint signal_id;
+ info = (_ExtendedGDBusSignalInfo *) g_dbus_interface_info_lookup_signal ((GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct, signal_name);
+ if (info == NULL)
+ return;
+ num_params = g_variant_n_children (parameters);
+ paramv = g_new0 (GValue, num_params + 1);
+ g_value_init (&paramv[0], GIMP_DBUS_SERVICE_TYPE_UI);
+ g_value_set_object (&paramv[0], proxy);
+ g_variant_iter_init (&iter, parameters);
+ n = 1;
+ while ((child = g_variant_iter_next_value (&iter)) != NULL)
+ {
+ _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.args[n - 1];
+ if (arg_info->use_gvariant)
+ {
+ g_value_init (&paramv[n], G_TYPE_VARIANT);
+ g_value_set_variant (&paramv[n], child);
+ n++;
+ }
+ else
+ g_dbus_gvariant_to_gvalue (child, &paramv[n++]);
+ g_variant_unref (child);
+ }
+ signal_id = g_signal_lookup (info->signal_name, GIMP_DBUS_SERVICE_TYPE_UI);
+ g_signal_emitv (paramv, signal_id, 0, NULL);
+ for (n = 0; n < num_params + 1; n++)
+ g_value_unset (&paramv[n]);
+ g_free (paramv);
+}
+
+static void
+gimp_dbus_service_ui_proxy_g_properties_changed (GDBusProxy *_proxy,
+ GVariant *changed_properties,
+ const gchar *const *invalidated_properties)
+{
+ GimpDBusServiceUIProxy *proxy = GIMP_DBUS_SERVICE_UI_PROXY (_proxy);
+ guint n;
+ const gchar *key;
+ GVariantIter *iter;
+ _ExtendedGDBusPropertyInfo *info;
+ g_variant_get (changed_properties, "a{sv}", &iter);
+ while (g_variant_iter_next (iter, "{&sv}", &key, NULL))
+ {
+ info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct, key);
+ g_datalist_remove_data (&proxy->priv->qdata, key);
+ if (info != NULL)
+ g_object_notify (G_OBJECT (proxy), info->hyphen_name);
+ }
+ g_variant_iter_free (iter);
+ for (n = 0; invalidated_properties[n] != NULL; n++)
+ {
+ info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct, invalidated_properties[n]);
+ g_datalist_remove_data (&proxy->priv->qdata, invalidated_properties[n]);
+ if (info != NULL)
+ g_object_notify (G_OBJECT (proxy), info->hyphen_name);
+ }
+}
+
+static void
+gimp_dbus_service_ui_proxy_init (GimpDBusServiceUIProxy *proxy)
+{
+#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38
+ proxy->priv = gimp_dbus_service_ui_proxy_get_instance_private (proxy);
+#else
+ proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, GIMP_DBUS_SERVICE_TYPE_UI_PROXY, GimpDBusServiceUIProxyPrivate);
+#endif
+
+ g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), gimp_dbus_service_ui_interface_info ());
+}
+
+static void
+gimp_dbus_service_ui_proxy_class_init (GimpDBusServiceUIProxyClass *klass)
+{
+ GObjectClass *gobject_class;
+ GDBusProxyClass *proxy_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = gimp_dbus_service_ui_proxy_finalize;
+ gobject_class->get_property = gimp_dbus_service_ui_proxy_get_property;
+ gobject_class->set_property = gimp_dbus_service_ui_proxy_set_property;
+
+ proxy_class = G_DBUS_PROXY_CLASS (klass);
+ proxy_class->g_signal = gimp_dbus_service_ui_proxy_g_signal;
+ proxy_class->g_properties_changed = gimp_dbus_service_ui_proxy_g_properties_changed;
+
+#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38
+ g_type_class_add_private (klass, sizeof (GimpDBusServiceUIProxyPrivate));
+#endif
+}
+
+static void
+gimp_dbus_service_ui_proxy_iface_init (GimpDBusServiceUIIface *iface)
+{
+}
+
+/**
+ * gimp_dbus_service_ui_proxy_new:
+ * @connection: A #GDBusConnection.
+ * @flags: Flags from the #GDBusProxyFlags enumeration.
+ * @name: (nullable): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection.
+ * @object_path: An object path.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously creates a proxy for the D-Bus interface <link linkend="gdbus-interface-org-gimp-GIMP-UI.top_of_page">org.gimp.GIMP.UI</link>. See g_dbus_proxy_new() for more details.
+ *
+ * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()).
+ * You can then call gimp_dbus_service_ui_proxy_new_finish() to get the result of the operation.
+ *
+ * See gimp_dbus_service_ui_proxy_new_sync() for the synchronous, blocking version of this constructor.
+ */
+void
+gimp_dbus_service_ui_proxy_new (
+ GDBusConnection *connection,
+ GDBusProxyFlags flags,
+ const gchar *name,
+ const gchar *object_path,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_async_initable_new_async (GIMP_DBUS_SERVICE_TYPE_UI_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.gimp.GIMP.UI", NULL);
+}
+
+/**
+ * gimp_dbus_service_ui_proxy_new_finish:
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_proxy_new().
+ * @error: Return location for error or %NULL
+ *
+ * Finishes an operation started with gimp_dbus_service_ui_proxy_new().
+ *
+ * Returns: (transfer full) (type GimpDBusServiceUIProxy): The constructed proxy object or %NULL if @error is set.
+ */
+GimpDBusServiceUI *
+gimp_dbus_service_ui_proxy_new_finish (
+ GAsyncResult *res,
+ GError **error)
+{
+ GObject *ret;
+ GObject *source_object;
+ source_object = g_async_result_get_source_object (res);
+ ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error);
+ g_object_unref (source_object);
+ if (ret != NULL)
+ return GIMP_DBUS_SERVICE_UI (ret);
+ else
+ return NULL;
+}
+
+/**
+ * gimp_dbus_service_ui_proxy_new_sync:
+ * @connection: A #GDBusConnection.
+ * @flags: Flags from the #GDBusProxyFlags enumeration.
+ * @name: (nullable): A bus name (well-known or unique) or %NULL if @connection is not a message bus connection.
+ * @object_path: An object path.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL
+ *
+ * Synchronously creates a proxy for the D-Bus interface <link linkend="gdbus-interface-org-gimp-GIMP-UI.top_of_page">org.gimp.GIMP.UI</link>. See g_dbus_proxy_new_sync() for more details.
+ *
+ * The calling thread is blocked until a reply is received.
+ *
+ * See gimp_dbus_service_ui_proxy_new() for the asynchronous version of this constructor.
+ *
+ * Returns: (transfer full) (type GimpDBusServiceUIProxy): The constructed proxy object or %NULL if @error is set.
+ */
+GimpDBusServiceUI *
+gimp_dbus_service_ui_proxy_new_sync (
+ GDBusConnection *connection,
+ GDBusProxyFlags flags,
+ const gchar *name,
+ const gchar *object_path,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GInitable *ret;
+ ret = g_initable_new (GIMP_DBUS_SERVICE_TYPE_UI_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", "org.gimp.GIMP.UI", NULL);
+ if (ret != NULL)
+ return GIMP_DBUS_SERVICE_UI (ret);
+ else
+ return NULL;
+}
+
+
+/**
+ * gimp_dbus_service_ui_proxy_new_for_bus:
+ * @bus_type: A #GBusType.
+ * @flags: Flags from the #GDBusProxyFlags enumeration.
+ * @name: A bus name (well-known or unique).
+ * @object_path: An object path.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
+ * @user_data: User data to pass to @callback.
+ *
+ * Like gimp_dbus_service_ui_proxy_new() but takes a #GBusType instead of a #GDBusConnection.
+ *
+ * When the operation is finished, @callback will be invoked in the thread-default main loop of the thread you are calling this method from (see g_main_context_push_thread_default()).
+ * You can then call gimp_dbus_service_ui_proxy_new_for_bus_finish() to get the result of the operation.
+ *
+ * See gimp_dbus_service_ui_proxy_new_for_bus_sync() for the synchronous, blocking version of this constructor.
+ */
+void
+gimp_dbus_service_ui_proxy_new_for_bus (
+ GBusType bus_type,
+ GDBusProxyFlags flags,
+ const gchar *name,
+ const gchar *object_path,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_async_initable_new_async (GIMP_DBUS_SERVICE_TYPE_UI_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.gimp.GIMP.UI", NULL);
+}
+
+/**
+ * gimp_dbus_service_ui_proxy_new_for_bus_finish:
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to gimp_dbus_service_ui_proxy_new_for_bus().
+ * @error: Return location for error or %NULL
+ *
+ * Finishes an operation started with gimp_dbus_service_ui_proxy_new_for_bus().
+ *
+ * Returns: (transfer full) (type GimpDBusServiceUIProxy): The constructed proxy object or %NULL if @error is set.
+ */
+GimpDBusServiceUI *
+gimp_dbus_service_ui_proxy_new_for_bus_finish (
+ GAsyncResult *res,
+ GError **error)
+{
+ GObject *ret;
+ GObject *source_object;
+ source_object = g_async_result_get_source_object (res);
+ ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error);
+ g_object_unref (source_object);
+ if (ret != NULL)
+ return GIMP_DBUS_SERVICE_UI (ret);
+ else
+ return NULL;
+}
+
+/**
+ * gimp_dbus_service_ui_proxy_new_for_bus_sync:
+ * @bus_type: A #GBusType.
+ * @flags: Flags from the #GDBusProxyFlags enumeration.
+ * @name: A bus name (well-known or unique).
+ * @object_path: An object path.
+ * @cancellable: (nullable): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL
+ *
+ * Like gimp_dbus_service_ui_proxy_new_sync() but takes a #GBusType instead of a #GDBusConnection.
+ *
+ * The calling thread is blocked until a reply is received.
+ *
+ * See gimp_dbus_service_ui_proxy_new_for_bus() for the asynchronous version of this constructor.
+ *
+ * Returns: (transfer full) (type GimpDBusServiceUIProxy): The constructed proxy object or %NULL if @error is set.
+ */
+GimpDBusServiceUI *
+gimp_dbus_service_ui_proxy_new_for_bus_sync (
+ GBusType bus_type,
+ GDBusProxyFlags flags,
+ const gchar *name,
+ const gchar *object_path,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GInitable *ret;
+ ret = g_initable_new (GIMP_DBUS_SERVICE_TYPE_UI_PROXY, cancellable, error, "g-flags", flags, "g-name", name, "g-bus-type", bus_type, "g-object-path", object_path, "g-interface-name", "org.gimp.GIMP.UI", NULL);
+ if (ret != NULL)
+ return GIMP_DBUS_SERVICE_UI (ret);
+ else
+ return NULL;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/**
+ * GimpDBusServiceUISkeleton:
+ *
+ * The #GimpDBusServiceUISkeleton structure contains only private data and should only be accessed using the provided API.
+ */
+
+/**
+ * GimpDBusServiceUISkeletonClass:
+ * @parent_class: The parent class.
+ *
+ * Class structure for #GimpDBusServiceUISkeleton.
+ */
+
+struct _GimpDBusServiceUISkeletonPrivate
+{
+ GValue *properties;
+ GList *changed_properties;
+ GSource *changed_properties_idle_source;
+ GMainContext *context;
+ GMutex lock;
+};
+
+static void
+_gimp_dbus_service_ui_skeleton_handle_method_call (
+ GDBusConnection *connection G_GNUC_UNUSED,
+ const gchar *sender G_GNUC_UNUSED,
+ const gchar *object_path G_GNUC_UNUSED,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (user_data);
+ _ExtendedGDBusMethodInfo *info;
+ GVariantIter iter;
+ GVariant *child;
+ GValue *paramv;
+ gsize num_params;
+ guint num_extra;
+ gsize n;
+ guint signal_id;
+ GValue return_value = G_VALUE_INIT;
+ info = (_ExtendedGDBusMethodInfo *) g_dbus_method_invocation_get_method_info (invocation);
+ g_assert (info != NULL);
+ num_params = g_variant_n_children (parameters);
+ num_extra = info->pass_fdlist ? 3 : 2; paramv = g_new0 (GValue, num_params + num_extra);
+ n = 0;
+ g_value_init (&paramv[n], GIMP_DBUS_SERVICE_TYPE_UI);
+ g_value_set_object (&paramv[n++], skeleton);
+ g_value_init (&paramv[n], G_TYPE_DBUS_METHOD_INVOCATION);
+ g_value_set_object (&paramv[n++], invocation);
+ if (info->pass_fdlist)
+ {
+#ifdef G_OS_UNIX
+ g_value_init (&paramv[n], G_TYPE_UNIX_FD_LIST);
+ g_value_set_object (&paramv[n++], g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation)));
+#else
+ g_assert_not_reached ();
+#endif
+ }
+ g_variant_iter_init (&iter, parameters);
+ while ((child = g_variant_iter_next_value (&iter)) != NULL)
+ {
+ _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - num_extra];
+ if (arg_info->use_gvariant)
+ {
+ g_value_init (&paramv[n], G_TYPE_VARIANT);
+ g_value_set_variant (&paramv[n], child);
+ n++;
+ }
+ else
+ g_dbus_gvariant_to_gvalue (child, &paramv[n++]);
+ g_variant_unref (child);
+ }
+ signal_id = g_signal_lookup (info->signal_name, GIMP_DBUS_SERVICE_TYPE_UI);
+ g_value_init (&return_value, G_TYPE_BOOLEAN);
+ g_signal_emitv (paramv, signal_id, 0, &return_value);
+ if (!g_value_get_boolean (&return_value))
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method %s is not implemented on interface %s", method_name, interface_name);
+ g_value_unset (&return_value);
+ for (n = 0; n < num_params + num_extra; n++)
+ g_value_unset (&paramv[n]);
+ g_free (paramv);
+}
+
+static GVariant *
+_gimp_dbus_service_ui_skeleton_handle_get_property (
+ GDBusConnection *connection G_GNUC_UNUSED,
+ const gchar *sender G_GNUC_UNUSED,
+ const gchar *object_path G_GNUC_UNUSED,
+ const gchar *interface_name G_GNUC_UNUSED,
+ const gchar *property_name,
+ GError **error,
+ gpointer user_data)
+{
+ GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (user_data);
+ GValue value = G_VALUE_INIT;
+ GParamSpec *pspec;
+ _ExtendedGDBusPropertyInfo *info;
+ GVariant *ret;
+ ret = NULL;
+ info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct, property_name);
+ g_assert (info != NULL);
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name);
+ if (pspec == NULL)
+ {
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name);
+ }
+ else
+ {
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (G_OBJECT (skeleton), info->hyphen_name, &value);
+ ret = g_dbus_gvalue_to_gvariant (&value, G_VARIANT_TYPE (info->parent_struct.signature));
+ g_value_unset (&value);
+ }
+ return ret;
+}
+
+static gboolean
+_gimp_dbus_service_ui_skeleton_handle_set_property (
+ GDBusConnection *connection G_GNUC_UNUSED,
+ const gchar *sender G_GNUC_UNUSED,
+ const gchar *object_path G_GNUC_UNUSED,
+ const gchar *interface_name G_GNUC_UNUSED,
+ const gchar *property_name,
+ GVariant *variant,
+ GError **error,
+ gpointer user_data)
+{
+ GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (user_data);
+ GValue value = G_VALUE_INIT;
+ GParamSpec *pspec;
+ _ExtendedGDBusPropertyInfo *info;
+ gboolean ret;
+ ret = FALSE;
+ info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_gimp_dbus_service_ui_interface_info.parent_struct, property_name);
+ g_assert (info != NULL);
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (skeleton), info->hyphen_name);
+ if (pspec == NULL)
+ {
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No property with name %s", property_name);
+ }
+ else
+ {
+ if (info->use_gvariant)
+ g_value_set_variant (&value, variant);
+ else
+ g_dbus_gvariant_to_gvalue (variant, &value);
+ g_object_set_property (G_OBJECT (skeleton), info->hyphen_name, &value);
+ g_value_unset (&value);
+ ret = TRUE;
+ }
+ return ret;
+}
+
+static const GDBusInterfaceVTable _gimp_dbus_service_ui_skeleton_vtable =
+{
+ _gimp_dbus_service_ui_skeleton_handle_method_call,
+ _gimp_dbus_service_ui_skeleton_handle_get_property,
+ _gimp_dbus_service_ui_skeleton_handle_set_property,
+ {NULL}
+};
+
+static GDBusInterfaceInfo *
+gimp_dbus_service_ui_skeleton_dbus_interface_get_info (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED)
+{
+ return gimp_dbus_service_ui_interface_info ();
+}
+
+static GDBusInterfaceVTable *
+gimp_dbus_service_ui_skeleton_dbus_interface_get_vtable (GDBusInterfaceSkeleton *skeleton G_GNUC_UNUSED)
+{
+ return (GDBusInterfaceVTable *) &_gimp_dbus_service_ui_skeleton_vtable;
+}
+
+static GVariant *
+gimp_dbus_service_ui_skeleton_dbus_interface_get_properties (GDBusInterfaceSkeleton *_skeleton)
+{
+ GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (_skeleton);
+
+ GVariantBuilder builder;
+ guint n;
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ if (_gimp_dbus_service_ui_interface_info.parent_struct.properties == NULL)
+ goto out;
+ for (n = 0; _gimp_dbus_service_ui_interface_info.parent_struct.properties[n] != NULL; n++)
+ {
+ GDBusPropertyInfo *info = _gimp_dbus_service_ui_interface_info.parent_struct.properties[n];
+ if (info->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
+ {
+ GVariant *value;
+ value = _gimp_dbus_service_ui_skeleton_handle_get_property (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)), NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.gimp.GIMP.UI", info->name, NULL, skeleton);
+ if (value != NULL)
+ {
+ g_variant_take_ref (value);
+ g_variant_builder_add (&builder, "{sv}", info->name, value);
+ g_variant_unref (value);
+ }
+ }
+ }
+out:
+ return g_variant_builder_end (&builder);
+}
+
+static void
+gimp_dbus_service_ui_skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton)
+{
+}
+
+static void
+_gimp_dbus_service_ui_on_signal_opened (
+ GimpDBusServiceUI *object,
+ const gchar *arg_uri)
+{
+ GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (object);
+
+ GList *connections, *l;
+ GVariant *signal_variant;
+ connections = g_dbus_interface_skeleton_get_connections (G_DBUS_INTERFACE_SKELETON (skeleton));
+
+ signal_variant = g_variant_ref_sink (g_variant_new ("(s)",
+ arg_uri));
+ for (l = connections; l != NULL; l = l->next)
+ {
+ GDBusConnection *connection = l->data;
+ g_dbus_connection_emit_signal (connection,
+ NULL, g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (skeleton)), "org.gimp.GIMP.UI", "Opened",
+ signal_variant, NULL);
+ }
+ g_variant_unref (signal_variant);
+ g_list_free_full (connections, g_object_unref);
+}
+
+static void gimp_dbus_service_ui_skeleton_iface_init (GimpDBusServiceUIIface *iface);
+#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38
+G_DEFINE_TYPE_WITH_CODE (GimpDBusServiceUISkeleton, gimp_dbus_service_ui_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON,
+ G_ADD_PRIVATE (GimpDBusServiceUISkeleton)
+ G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI, gimp_dbus_service_ui_skeleton_iface_init))
+
+#else
+G_DEFINE_TYPE_WITH_CODE (GimpDBusServiceUISkeleton, gimp_dbus_service_ui_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON,
+ G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI, gimp_dbus_service_ui_skeleton_iface_init))
+
+#endif
+static void
+gimp_dbus_service_ui_skeleton_finalize (GObject *object)
+{
+ GimpDBusServiceUISkeleton *skeleton = GIMP_DBUS_SERVICE_UI_SKELETON (object);
+ g_list_free_full (skeleton->priv->changed_properties, (GDestroyNotify) _changed_property_free);
+ if (skeleton->priv->changed_properties_idle_source != NULL)
+ g_source_destroy (skeleton->priv->changed_properties_idle_source);
+ g_main_context_unref (skeleton->priv->context);
+ g_mutex_clear (&skeleton->priv->lock);
+ G_OBJECT_CLASS (gimp_dbus_service_ui_skeleton_parent_class)->finalize (object);
+}
+
+static void
+gimp_dbus_service_ui_skeleton_init (GimpDBusServiceUISkeleton *skeleton)
+{
+#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38
+ skeleton->priv = gimp_dbus_service_ui_skeleton_get_instance_private (skeleton);
+#else
+ skeleton->priv = G_TYPE_INSTANCE_GET_PRIVATE (skeleton, GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, GimpDBusServiceUISkeletonPrivate);
+#endif
+
+ g_mutex_init (&skeleton->priv->lock);
+ skeleton->priv->context = g_main_context_ref_thread_default ();
+}
+
+static void
+gimp_dbus_service_ui_skeleton_class_init (GimpDBusServiceUISkeletonClass *klass)
+{
+ GObjectClass *gobject_class;
+ GDBusInterfaceSkeletonClass *skeleton_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = gimp_dbus_service_ui_skeleton_finalize;
+
+ skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass);
+ skeleton_class->get_info = gimp_dbus_service_ui_skeleton_dbus_interface_get_info;
+ skeleton_class->get_properties = gimp_dbus_service_ui_skeleton_dbus_interface_get_properties;
+ skeleton_class->flush = gimp_dbus_service_ui_skeleton_dbus_interface_flush;
+ skeleton_class->get_vtable = gimp_dbus_service_ui_skeleton_dbus_interface_get_vtable;
+
+#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38
+ g_type_class_add_private (klass, sizeof (GimpDBusServiceUISkeletonPrivate));
+#endif
+}
+
+static void
+gimp_dbus_service_ui_skeleton_iface_init (GimpDBusServiceUIIface *iface)
+{
+ iface->opened = _gimp_dbus_service_ui_on_signal_opened;
+}
+
+/**
+ * gimp_dbus_service_ui_skeleton_new:
+ *
+ * Creates a skeleton object for the D-Bus interface <link linkend="gdbus-interface-org-gimp-GIMP-UI.top_of_page">org.gimp.GIMP.UI</link>.
+ *
+ * Returns: (transfer full) (type GimpDBusServiceUISkeleton): The skeleton object.
+ */
+GimpDBusServiceUI *
+gimp_dbus_service_ui_skeleton_new (void)
+{
+ return GIMP_DBUS_SERVICE_UI (g_object_new (GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, NULL));
+}
+
diff --git a/app/gui/gimpdbusservice-generated.h b/app/gui/gimpdbusservice-generated.h
new file mode 100644
index 0000000..5c8c465
--- /dev/null
+++ b/app/gui/gimpdbusservice-generated.h
@@ -0,0 +1,282 @@
+/*
+ * This file is generated by gdbus-codegen, do not modify it.
+ *
+ * The license of this code is the same as for the D-Bus interface description
+ * it was derived from. Note that it links to GLib, so must comply with the
+ * LGPL linking clauses.
+ */
+
+#ifndef __GIMPDBUSSERVICE_GENERATED_H__
+#define __GIMPDBUSSERVICE_GENERATED_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+
+/* ------------------------------------------------------------------------ */
+/* Declarations for org.gimp.GIMP.UI */
+
+#define GIMP_DBUS_SERVICE_TYPE_UI (gimp_dbus_service_ui_get_type ())
+#define GIMP_DBUS_SERVICE_UI(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GIMP_DBUS_SERVICE_TYPE_UI, GimpDBusServiceUI))
+#define GIMP_DBUS_SERVICE_IS_UI(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GIMP_DBUS_SERVICE_TYPE_UI))
+#define GIMP_DBUS_SERVICE_UI_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GIMP_DBUS_SERVICE_TYPE_UI, GimpDBusServiceUIIface))
+
+struct _GimpDBusServiceUI;
+typedef struct _GimpDBusServiceUI GimpDBusServiceUI;
+typedef struct _GimpDBusServiceUIIface GimpDBusServiceUIIface;
+
+struct _GimpDBusServiceUIIface
+{
+ GTypeInterface parent_iface;
+
+
+ gboolean (*handle_activate) (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation);
+
+ gboolean (*handle_batch_run) (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation,
+ const gchar *arg_interpreter,
+ const gchar *arg_command);
+
+ gboolean (*handle_open) (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation,
+ const gchar *arg_uri);
+
+ gboolean (*handle_open_as_new) (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation,
+ const gchar *arg_uri);
+
+ void (*opened) (
+ GimpDBusServiceUI *object,
+ const gchar *arg_uri);
+
+};
+
+GType gimp_dbus_service_ui_get_type (void) G_GNUC_CONST;
+
+GDBusInterfaceInfo *gimp_dbus_service_ui_interface_info (void);
+guint gimp_dbus_service_ui_override_properties (GObjectClass *klass, guint property_id_begin);
+
+
+/* D-Bus method call completion functions: */
+void gimp_dbus_service_ui_complete_open (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation,
+ gboolean success);
+
+void gimp_dbus_service_ui_complete_open_as_new (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation,
+ gboolean success);
+
+void gimp_dbus_service_ui_complete_batch_run (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation,
+ gboolean success);
+
+void gimp_dbus_service_ui_complete_activate (
+ GimpDBusServiceUI *object,
+ GDBusMethodInvocation *invocation);
+
+
+
+/* D-Bus signal emissions functions: */
+void gimp_dbus_service_ui_emit_opened (
+ GimpDBusServiceUI *object,
+ const gchar *arg_uri);
+
+
+
+/* D-Bus method calls: */
+void gimp_dbus_service_ui_call_open (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_uri,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean gimp_dbus_service_ui_call_open_finish (
+ GimpDBusServiceUI *proxy,
+ gboolean *out_success,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean gimp_dbus_service_ui_call_open_sync (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_uri,
+ gboolean *out_success,
+ GCancellable *cancellable,
+ GError **error);
+
+void gimp_dbus_service_ui_call_open_as_new (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_uri,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean gimp_dbus_service_ui_call_open_as_new_finish (
+ GimpDBusServiceUI *proxy,
+ gboolean *out_success,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean gimp_dbus_service_ui_call_open_as_new_sync (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_uri,
+ gboolean *out_success,
+ GCancellable *cancellable,
+ GError **error);
+
+void gimp_dbus_service_ui_call_batch_run (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_interpreter,
+ const gchar *arg_command,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean gimp_dbus_service_ui_call_batch_run_finish (
+ GimpDBusServiceUI *proxy,
+ gboolean *out_success,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean gimp_dbus_service_ui_call_batch_run_sync (
+ GimpDBusServiceUI *proxy,
+ const gchar *arg_interpreter,
+ const gchar *arg_command,
+ gboolean *out_success,
+ GCancellable *cancellable,
+ GError **error);
+
+void gimp_dbus_service_ui_call_activate (
+ GimpDBusServiceUI *proxy,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean gimp_dbus_service_ui_call_activate_finish (
+ GimpDBusServiceUI *proxy,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean gimp_dbus_service_ui_call_activate_sync (
+ GimpDBusServiceUI *proxy,
+ GCancellable *cancellable,
+ GError **error);
+
+
+
+/* ---- */
+
+#define GIMP_DBUS_SERVICE_TYPE_UI_PROXY (gimp_dbus_service_ui_proxy_get_type ())
+#define GIMP_DBUS_SERVICE_UI_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GIMP_DBUS_SERVICE_TYPE_UI_PROXY, GimpDBusServiceUIProxy))
+#define GIMP_DBUS_SERVICE_UI_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GIMP_DBUS_SERVICE_TYPE_UI_PROXY, GimpDBusServiceUIProxyClass))
+#define GIMP_DBUS_SERVICE_UI_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GIMP_DBUS_SERVICE_TYPE_UI_PROXY, GimpDBusServiceUIProxyClass))
+#define GIMP_DBUS_SERVICE_IS_UI_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GIMP_DBUS_SERVICE_TYPE_UI_PROXY))
+#define GIMP_DBUS_SERVICE_IS_UI_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GIMP_DBUS_SERVICE_TYPE_UI_PROXY))
+
+typedef struct _GimpDBusServiceUIProxy GimpDBusServiceUIProxy;
+typedef struct _GimpDBusServiceUIProxyClass GimpDBusServiceUIProxyClass;
+typedef struct _GimpDBusServiceUIProxyPrivate GimpDBusServiceUIProxyPrivate;
+
+struct _GimpDBusServiceUIProxy
+{
+ /*< private >*/
+ GDBusProxy parent_instance;
+ GimpDBusServiceUIProxyPrivate *priv;
+};
+
+struct _GimpDBusServiceUIProxyClass
+{
+ GDBusProxyClass parent_class;
+};
+
+GType gimp_dbus_service_ui_proxy_get_type (void) G_GNUC_CONST;
+
+#if GLIB_CHECK_VERSION(2, 44, 0)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GimpDBusServiceUIProxy, g_object_unref)
+#endif
+
+void gimp_dbus_service_ui_proxy_new (
+ GDBusConnection *connection,
+ GDBusProxyFlags flags,
+ const gchar *name,
+ const gchar *object_path,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GimpDBusServiceUI *gimp_dbus_service_ui_proxy_new_finish (
+ GAsyncResult *res,
+ GError **error);
+GimpDBusServiceUI *gimp_dbus_service_ui_proxy_new_sync (
+ GDBusConnection *connection,
+ GDBusProxyFlags flags,
+ const gchar *name,
+ const gchar *object_path,
+ GCancellable *cancellable,
+ GError **error);
+
+void gimp_dbus_service_ui_proxy_new_for_bus (
+ GBusType bus_type,
+ GDBusProxyFlags flags,
+ const gchar *name,
+ const gchar *object_path,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GimpDBusServiceUI *gimp_dbus_service_ui_proxy_new_for_bus_finish (
+ GAsyncResult *res,
+ GError **error);
+GimpDBusServiceUI *gimp_dbus_service_ui_proxy_new_for_bus_sync (
+ GBusType bus_type,
+ GDBusProxyFlags flags,
+ const gchar *name,
+ const gchar *object_path,
+ GCancellable *cancellable,
+ GError **error);
+
+
+/* ---- */
+
+#define GIMP_DBUS_SERVICE_TYPE_UI_SKELETON (gimp_dbus_service_ui_skeleton_get_type ())
+#define GIMP_DBUS_SERVICE_UI_SKELETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, GimpDBusServiceUISkeleton))
+#define GIMP_DBUS_SERVICE_UI_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, GimpDBusServiceUISkeletonClass))
+#define GIMP_DBUS_SERVICE_UI_SKELETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, GimpDBusServiceUISkeletonClass))
+#define GIMP_DBUS_SERVICE_IS_UI_SKELETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GIMP_DBUS_SERVICE_TYPE_UI_SKELETON))
+#define GIMP_DBUS_SERVICE_IS_UI_SKELETON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GIMP_DBUS_SERVICE_TYPE_UI_SKELETON))
+
+typedef struct _GimpDBusServiceUISkeleton GimpDBusServiceUISkeleton;
+typedef struct _GimpDBusServiceUISkeletonClass GimpDBusServiceUISkeletonClass;
+typedef struct _GimpDBusServiceUISkeletonPrivate GimpDBusServiceUISkeletonPrivate;
+
+struct _GimpDBusServiceUISkeleton
+{
+ /*< private >*/
+ GDBusInterfaceSkeleton parent_instance;
+ GimpDBusServiceUISkeletonPrivate *priv;
+};
+
+struct _GimpDBusServiceUISkeletonClass
+{
+ GDBusInterfaceSkeletonClass parent_class;
+};
+
+GType gimp_dbus_service_ui_skeleton_get_type (void) G_GNUC_CONST;
+
+#if GLIB_CHECK_VERSION(2, 44, 0)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GimpDBusServiceUISkeleton, g_object_unref)
+#endif
+
+GimpDBusServiceUI *gimp_dbus_service_ui_skeleton_new (void);
+
+
+G_END_DECLS
+
+#endif /* __GIMPDBUSSERVICE_GENERATED_H__ */
diff --git a/app/gui/gimpdbusservice.c b/app/gui/gimpdbusservice.c
new file mode 100644
index 0000000..462d7d4
--- /dev/null
+++ b/app/gui/gimpdbusservice.c
@@ -0,0 +1,457 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpDBusService
+ * Copyright (C) 2007, 2008 Sven Neumann <sven@gimp.org>
+ * Copyright (C) 2013 Michael Natterer <mitch@gimp.org>
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "gui-types.h"
+
+#include "core/gimp.h"
+#include "core/gimp-batch.h"
+#include "core/gimpcontainer.h"
+
+#include "file/file-open.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimpdisplayshell.h"
+
+#include "gimpdbusservice.h"
+
+
+typedef struct
+{
+ GFile *file;
+ gboolean as_new;
+
+ gchar *interpreter;
+ gchar *command;
+} IdleData;
+
+static void gimp_dbus_service_ui_iface_init (GimpDBusServiceUIIface *iface);
+
+static void gimp_dbus_service_dispose (GObject *object);
+static void gimp_dbus_service_finalize (GObject *object);
+
+static gboolean gimp_dbus_service_activate (GimpDBusServiceUI *service,
+ GDBusMethodInvocation *invocation);
+static gboolean gimp_dbus_service_open (GimpDBusServiceUI *service,
+ GDBusMethodInvocation *invocation,
+ const gchar *uri);
+
+static gboolean gimp_dbus_service_open_as_new (GimpDBusServiceUI *service,
+ GDBusMethodInvocation *invocation,
+ const gchar *uri);
+
+static gboolean gimp_dbus_service_batch_run (GimpDBusServiceUI *service,
+ GDBusMethodInvocation *invocation,
+ const gchar *batch_interpreter,
+ const gchar *batch_command);
+
+static void gimp_dbus_service_gimp_opened (Gimp *gimp,
+ GFile *file,
+ GimpDBusService *service);
+
+static gboolean gimp_dbus_service_queue_open (GimpDBusService *service,
+ const gchar *uri,
+ gboolean as_new);
+static gboolean gimp_dbus_service_queue_batch (GimpDBusService *service,
+ const gchar *interpreter,
+ const gchar *command);
+
+static gboolean gimp_dbus_service_process_idle (GimpDBusService *service);
+static IdleData * gimp_dbus_service_open_data_new (GimpDBusService *service,
+ const gchar *uri,
+ gboolean as_new);
+static IdleData * gimp_dbus_service_batch_data_new (GimpDBusService *service,
+ const gchar *interpreter,
+ const gchar *command);
+static void gimp_dbus_service_idle_data_free (IdleData *data);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpDBusService, gimp_dbus_service,
+ GIMP_DBUS_SERVICE_TYPE_UI_SKELETON,
+ G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI,
+ gimp_dbus_service_ui_iface_init))
+
+#define parent_class gimp_dbus_service_parent_class
+
+
+static void
+gimp_dbus_service_class_init (GimpDBusServiceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gimp_dbus_service_dispose;
+ object_class->finalize = gimp_dbus_service_finalize;
+}
+
+static void
+gimp_dbus_service_init (GimpDBusService *service)
+{
+ service->queue = g_queue_new ();
+}
+
+static void
+gimp_dbus_service_ui_iface_init (GimpDBusServiceUIIface *iface)
+{
+ iface->handle_activate = gimp_dbus_service_activate;
+ iface->handle_open = gimp_dbus_service_open;
+ iface->handle_open_as_new = gimp_dbus_service_open_as_new;
+ iface->handle_batch_run = gimp_dbus_service_batch_run;
+}
+
+GObject *
+gimp_dbus_service_new (Gimp *gimp)
+{
+ GimpDBusService *service;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+
+ service = g_object_new (GIMP_TYPE_DBUS_SERVICE, NULL);
+
+ service->gimp = gimp;
+
+ g_signal_connect_object (gimp, "image-opened",
+ G_CALLBACK (gimp_dbus_service_gimp_opened),
+ service, 0);
+
+ return G_OBJECT (service);
+}
+
+static void
+gimp_dbus_service_dispose (GObject *object)
+{
+ GimpDBusService *service = GIMP_DBUS_SERVICE (object);
+
+ if (service->source)
+ {
+ g_source_remove (g_source_get_id (service->source));
+ service->source = NULL;
+ service->timeout_source = FALSE;
+ }
+
+ while (! g_queue_is_empty (service->queue))
+ {
+ gimp_dbus_service_idle_data_free (g_queue_pop_head (service->queue));
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_dbus_service_finalize (GObject *object)
+{
+ GimpDBusService *service = GIMP_DBUS_SERVICE (object);
+
+ if (service->queue)
+ {
+ g_queue_free (service->queue);
+ service->queue = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+gboolean
+gimp_dbus_service_activate (GimpDBusServiceUI *service,
+ GDBusMethodInvocation *invocation)
+{
+ Gimp *gimp = GIMP_DBUS_SERVICE (service)->gimp;
+
+ /* We want to be called again later in case that GIMP is not fully
+ * started yet.
+ */
+ if (gimp_is_restored (gimp))
+ {
+ GimpObject *display;
+
+ display = gimp_container_get_first_child (gimp->displays);
+
+ if (display)
+ gimp_display_shell_present (gimp_display_get_shell (GIMP_DISPLAY (display)));
+ }
+
+ gimp_dbus_service_ui_complete_activate (service, invocation);
+
+ return TRUE;
+}
+
+gboolean
+gimp_dbus_service_open (GimpDBusServiceUI *service,
+ GDBusMethodInvocation *invocation,
+ const gchar *uri)
+{
+ gboolean success;
+
+ success = gimp_dbus_service_queue_open (GIMP_DBUS_SERVICE (service),
+ uri, FALSE);
+
+ gimp_dbus_service_ui_complete_open (service, invocation, success);
+
+ return TRUE;
+}
+
+gboolean
+gimp_dbus_service_open_as_new (GimpDBusServiceUI *service,
+ GDBusMethodInvocation *invocation,
+ const gchar *uri)
+{
+ gboolean success;
+
+ success = gimp_dbus_service_queue_open (GIMP_DBUS_SERVICE (service),
+ uri, TRUE);
+
+ gimp_dbus_service_ui_complete_open_as_new (service, invocation, success);
+
+ return TRUE;
+}
+
+gboolean
+gimp_dbus_service_batch_run (GimpDBusServiceUI *service,
+ GDBusMethodInvocation *invocation,
+ const gchar *batch_interpreter,
+ const gchar *batch_command)
+{
+ gboolean success;
+
+ success = gimp_dbus_service_queue_batch (GIMP_DBUS_SERVICE (service),
+ batch_interpreter,
+ batch_command);
+
+ gimp_dbus_service_ui_complete_batch_run (service, invocation, success);
+
+ return TRUE;
+}
+
+static void
+gimp_dbus_service_gimp_opened (Gimp *gimp,
+ GFile *file,
+ GimpDBusService *service)
+{
+ gchar *uri = g_file_get_uri (file);
+
+ g_signal_emit_by_name (service, "opened", uri);
+
+ g_free (uri);
+}
+
+/*
+ * Adds a request to open a file to the end of the queue and
+ * starts an idle source if it is not already running.
+ */
+static gboolean
+gimp_dbus_service_queue_open (GimpDBusService *service,
+ const gchar *uri,
+ gboolean as_new)
+{
+ g_queue_push_tail (service->queue,
+ gimp_dbus_service_open_data_new (service, uri, as_new));
+
+ if (! service->source)
+ {
+ service->source = g_idle_source_new ();
+ service->timeout_source = FALSE;
+
+ g_source_set_priority (service->source, G_PRIORITY_LOW);
+ g_source_set_callback (service->source,
+ (GSourceFunc) gimp_dbus_service_process_idle,
+ service,
+ NULL);
+ g_source_attach (service->source, NULL);
+ g_source_unref (service->source);
+ }
+
+ /* The call always succeeds as it is handled in one way or another.
+ * Even presenting an error message is considered success ;-)
+ */
+ return TRUE;
+}
+
+/*
+ * Adds a request to run a batch command to the end of the queue and
+ * starts an idle source if it is not already running.
+ */
+static gboolean
+gimp_dbus_service_queue_batch (GimpDBusService *service,
+ const gchar *interpreter,
+ const gchar *command)
+{
+ g_queue_push_tail (service->queue,
+ gimp_dbus_service_batch_data_new (service,
+ interpreter,
+ command));
+
+ if (! service->source)
+ {
+ service->source = g_idle_source_new ();
+ service->timeout_source = FALSE;
+
+ g_source_set_priority (service->source, G_PRIORITY_LOW);
+ g_source_set_callback (service->source,
+ (GSourceFunc) gimp_dbus_service_process_idle,
+ service,
+ NULL);
+ g_source_attach (service->source, NULL);
+ g_source_unref (service->source);
+ }
+
+ /* The call always succeeds as it is handled in one way or another.
+ * Even presenting an error message is considered success ;-)
+ */
+ return TRUE;
+}
+
+/*
+ * Idle callback that removes the first request from the queue and
+ * handles it. If there are no more requests, the idle source is
+ * removed.
+ */
+static gboolean
+gimp_dbus_service_process_idle (GimpDBusService *service)
+{
+ IdleData *data;
+
+ if (! service->gimp->initialized || ! service->gimp->restored)
+ {
+ if (! service->timeout_source)
+ {
+ /* We are probably starting the program. No need to spam GIMP with
+ * an idle handler (which might make GIMP slower to start even
+ * with low priority).
+ * Instead let's add a timeout of half a second.
+ */
+ service->source = g_timeout_source_new (500);
+ service->timeout_source = TRUE;
+
+ g_source_set_priority (service->source, G_PRIORITY_LOW);
+ g_source_set_callback (service->source,
+ (GSourceFunc) gimp_dbus_service_process_idle,
+ service,
+ NULL);
+ g_source_attach (service->source, NULL);
+ g_source_unref (service->source);
+
+ return G_SOURCE_REMOVE;
+ }
+ else
+ {
+ return G_SOURCE_CONTINUE;
+ }
+ }
+
+ /* Process data as a FIFO. */
+ data = g_queue_pop_head (service->queue);
+
+ if (data)
+ {
+ if (data->file)
+ file_open_from_command_line (service->gimp, data->file, data->as_new,
+ NULL, /* FIXME monitor */
+ 0 /* FIXME monitor */);
+ if (data->command)
+ {
+ const gchar *commands[2] = {data->command, 0};
+
+ gimp_batch_run (service->gimp, data->interpreter,
+ commands);
+ }
+
+ gimp_dbus_service_idle_data_free (data);
+
+ if (service->timeout_source)
+ {
+ /* Now GIMP is fully functional and can respond quickly to
+ * DBus calls. Switch to a usual idle source.
+ */
+ service->source = g_idle_source_new ();
+ service->timeout_source = FALSE;
+
+ g_source_set_priority (service->source, G_PRIORITY_LOW);
+ g_source_set_callback (service->source,
+ (GSourceFunc) gimp_dbus_service_process_idle,
+ service,
+ NULL);
+ g_source_attach (service->source, NULL);
+ g_source_unref (service->source);
+
+ return G_SOURCE_REMOVE;
+ }
+ else
+ {
+ return G_SOURCE_CONTINUE;
+ }
+ }
+
+ service->source = NULL;
+
+ return G_SOURCE_REMOVE;
+}
+
+static IdleData *
+gimp_dbus_service_open_data_new (GimpDBusService *service,
+ const gchar *uri,
+ gboolean as_new)
+{
+ IdleData *data = g_slice_new (IdleData);
+
+ data->file = g_file_new_for_uri (uri);
+ data->as_new = as_new;
+ data->interpreter = NULL;
+ data->command = NULL;
+
+ return data;
+}
+
+static IdleData *
+gimp_dbus_service_batch_data_new (GimpDBusService *service,
+ const gchar *interpreter,
+ const gchar *command)
+{
+ IdleData *data = g_slice_new (IdleData);
+
+ data->file = NULL;
+ data->as_new = FALSE;
+
+ data->command = g_strdup (command);
+
+ if (g_strcmp0 (interpreter, "") == 0)
+ data->interpreter = NULL;
+ else
+ data->interpreter = g_strdup (interpreter);
+
+ return data;
+}
+
+static void
+gimp_dbus_service_idle_data_free (IdleData *data)
+{
+ if (data->file)
+ g_object_unref (data->file);
+
+ if (data->command)
+ g_free (data->command);
+ if (data->interpreter)
+ g_free (data->interpreter);
+
+ g_slice_free (IdleData, data);
+}
diff --git a/app/gui/gimpdbusservice.h b/app/gui/gimpdbusservice.h
new file mode 100644
index 0000000..71f1493
--- /dev/null
+++ b/app/gui/gimpdbusservice.h
@@ -0,0 +1,69 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpDBusService
+ * Copyright (C) 2007, 2008 Sven Neumann <sven@gimp.org>
+ * Copyright (C) 2013 Michael Natterer <mitch@gimp.org>
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_DBUS_SERVICE_H__
+#define __GIMP_DBUS_SERVICE_H__
+
+
+#include "gimpdbusservice-generated.h"
+
+/* service name and path should really be org.gimp.GIMP and
+ * /org/gimp/GIMP and only the interface be called UI.
+ */
+#define GIMP_DBUS_SERVICE_NAME "org.gimp.GIMP.UI"
+#define GIMP_DBUS_SERVICE_PATH "/org/gimp/GIMP/UI"
+#define GIMP_DBUS_INTERFACE_NAME "org.gimp.GIMP.UI"
+#define GIMP_DBUS_INTERFACE_PATH "/org/gimp/GIMP/UI"
+
+
+#define GIMP_TYPE_DBUS_SERVICE (gimp_dbus_service_get_type ())
+#define GIMP_DBUS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DBUS_SERVICE, GimpDBusService))
+#define GIMP_DBUS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DBUS_SERVICE, GimpDBusServiceClass))
+#define GIMP_IS_DBUS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DBUS_SERVICE))
+#define GIMP_IS_DBUS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DBUS_SERVICE))
+#define GIMP_DBUS_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DBUS_SERVICE, GimpDBusServiceClass))
+
+
+typedef struct _GimpDBusService GimpDBusService;
+typedef struct _GimpDBusServiceClass GimpDBusServiceClass;
+
+struct _GimpDBusService
+{
+ GimpDBusServiceUISkeleton parent_instance;
+
+ Gimp *gimp;
+ GQueue *queue;
+ GSource *source;
+ gboolean timeout_source;
+};
+
+struct _GimpDBusServiceClass
+{
+ GimpDBusServiceUISkeletonClass parent_class;
+};
+
+
+GType gimp_dbus_service_get_type (void) G_GNUC_CONST;
+
+GObject * gimp_dbus_service_new (Gimp *gimp);
+
+
+#endif /* __GIMP_DBUS_SERVICE_H__ */
diff --git a/app/gui/gimpuiconfigurer.c b/app/gui/gimpuiconfigurer.c
new file mode 100644
index 0000000..dd9aa5d
--- /dev/null
+++ b/app/gui/gimpuiconfigurer.c
@@ -0,0 +1,622 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpuiconfigurer.c
+ * Copyright (C) 2009 Martin Nordholts <martinn@src.gnome.org>
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gui-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpcontext.h"
+
+#include "widgets/gimpdialogfactory.h"
+#include "widgets/gimpdock.h"
+#include "widgets/gimpdockcolumns.h"
+#include "widgets/gimpdockcontainer.h"
+#include "widgets/gimpdockwindow.h"
+#include "widgets/gimptoolbox.h"
+#include "widgets/gimpwidgets-utils.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimpdisplayshell.h"
+#include "display/gimpdisplayshell-appearance.h"
+#include "display/gimpimagewindow.h"
+
+#include "gimpuiconfigurer.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_GIMP
+};
+
+
+struct _GimpUIConfigurerPrivate
+{
+ Gimp *gimp;
+};
+
+
+static void gimp_ui_configurer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_ui_configurer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_ui_configurer_move_docks_to_columns (GimpUIConfigurer *ui_configurer,
+ GimpImageWindow *uber_image_window);
+static void gimp_ui_configurer_move_shells (GimpUIConfigurer *ui_configurer,
+ GimpImageWindow *source_image_window,
+ GimpImageWindow *target_image_window);
+static void gimp_ui_configurer_separate_docks (GimpUIConfigurer *ui_configurer,
+ GimpImageWindow *source_image_window);
+static void gimp_ui_configurer_move_docks_to_window (GimpUIConfigurer *ui_configurer,
+ GimpDockColumns *dock_columns,
+ GimpAlignmentType screen_side);
+static void gimp_ui_configurer_separate_shells (GimpUIConfigurer *ui_configurer,
+ GimpImageWindow *source_image_window);
+static void gimp_ui_configurer_configure_for_single_window (GimpUIConfigurer *ui_configurer);
+static void gimp_ui_configurer_configure_for_multi_window (GimpUIConfigurer *ui_configurer);
+static GimpImageWindow * gimp_ui_configurer_get_uber_window (GimpUIConfigurer *ui_configurer);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpUIConfigurer, gimp_ui_configurer,
+ GIMP_TYPE_OBJECT)
+
+#define parent_class gimp_ui_configurer_parent_class
+
+
+static void
+gimp_ui_configurer_class_init (GimpUIConfigurerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gimp_ui_configurer_set_property;
+ object_class->get_property = gimp_ui_configurer_get_property;
+
+ g_object_class_install_property (object_class, PROP_GIMP,
+ g_param_spec_object ("gimp", NULL, NULL,
+ GIMP_TYPE_GIMP,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_ui_configurer_init (GimpUIConfigurer *ui_configurer)
+{
+ ui_configurer->p = gimp_ui_configurer_get_instance_private (ui_configurer);
+}
+
+static void
+gimp_ui_configurer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpUIConfigurer *ui_configurer = GIMP_UI_CONFIGURER (object);
+
+ switch (property_id)
+ {
+ case PROP_GIMP:
+ ui_configurer->p->gimp = g_value_get_object (value); /* don't ref */
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_ui_configurer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpUIConfigurer *ui_configurer = GIMP_UI_CONFIGURER (object);
+
+ switch (property_id)
+ {
+ case PROP_GIMP:
+ g_value_set_object (value, ui_configurer->p->gimp);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gimp_ui_configurer_get_window_center_pos (GtkWindow *window,
+ gint *out_x,
+ gint *out_y)
+{
+ gint x, y, w, h;
+ gtk_window_get_position (window, &x, &y);
+ gtk_window_get_size (window, &w, &h);
+
+ if (out_x)
+ *out_x = x + w / 2;
+ if (out_y)
+ *out_y = y + h / 2;
+}
+
+/**
+ * gimp_ui_configurer_get_relative_window_pos:
+ * @window_a:
+ * @window_b:
+ *
+ * Returns: At what side @window_b is relative to @window_a. Either
+ * GIMP_ALIGN_LEFT or GIMP_ALIGN_RIGHT.
+ **/
+static GimpAlignmentType
+gimp_ui_configurer_get_relative_window_pos (GtkWindow *window_a,
+ GtkWindow *window_b)
+{
+ gint a_x, b_x;
+
+ gimp_ui_configurer_get_window_center_pos (window_a, &a_x, NULL);
+ gimp_ui_configurer_get_window_center_pos (window_b, &b_x, NULL);
+
+ return b_x < a_x ? GIMP_ALIGN_LEFT : GIMP_ALIGN_RIGHT;
+}
+
+static void
+gimp_ui_configurer_move_docks_to_columns (GimpUIConfigurer *ui_configurer,
+ GimpImageWindow *uber_image_window)
+{
+ GList *dialogs = NULL;
+ GList *dialog_iter = NULL;
+
+ dialogs =
+ g_list_copy (gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ()));
+
+ for (dialog_iter = dialogs; dialog_iter; dialog_iter = dialog_iter->next)
+ {
+ GimpDockWindow *dock_window;
+ GimpDockContainer *dock_container;
+ GimpDockColumns *dock_columns;
+ GList *docks;
+ GList *dock_iter;
+
+ if (!GIMP_IS_DOCK_WINDOW (dialog_iter->data))
+ continue;
+
+ dock_window = GIMP_DOCK_WINDOW (dialog_iter->data);
+
+ /* If the dock window is on the left side of the image window,
+ * move the docks to the left side. If the dock window is on the
+ * right side, move the docks to the right side of the image
+ * window.
+ */
+ if (gimp_ui_configurer_get_relative_window_pos (GTK_WINDOW (uber_image_window),
+ GTK_WINDOW (dock_window)) == GIMP_ALIGN_LEFT)
+ dock_columns = gimp_image_window_get_left_docks (uber_image_window);
+ else
+ dock_columns = gimp_image_window_get_right_docks (uber_image_window);
+
+ dock_container = GIMP_DOCK_CONTAINER (dock_window);
+ g_object_add_weak_pointer (G_OBJECT (dock_window),
+ (gpointer) &dock_window);
+
+ docks = gimp_dock_container_get_docks (dock_container);
+ for (dock_iter = docks; dock_iter; dock_iter = dock_iter->next)
+ {
+ GimpDock *dock = GIMP_DOCK (dock_iter->data);
+
+ /* Move the dock from the image window to the dock columns
+ * widget. Note that we need a ref while the dock is parentless
+ */
+ g_object_ref (dock);
+ gimp_dock_window_remove_dock (dock_window, dock);
+ gimp_dock_columns_add_dock (dock_columns, dock, -1);
+ g_object_unref (dock);
+ }
+ g_list_free (docks);
+
+ if (dock_window)
+ g_object_remove_weak_pointer (G_OBJECT (dock_window),
+ (gpointer) &dock_window);
+
+ /* Kill the window if removing the dock didn't destroy it
+ * already. This will be the case for the toolbox dock window
+ */
+ if (GTK_IS_WIDGET (dock_window))
+ {
+ guint docks_len;
+
+ docks = gimp_dock_container_get_docks (dock_container);
+ docks_len = g_list_length (docks);
+
+ if (docks_len == 0)
+ {
+ gimp_dialog_factory_remove_dialog (gimp_dialog_factory_get_singleton (),
+ GTK_WIDGET (dock_window));
+ gtk_widget_destroy (GTK_WIDGET (dock_window));
+ }
+
+ g_list_free (docks);
+ }
+ }
+
+ g_list_free (dialogs);
+}
+
+/**
+ * gimp_ui_configurer_move_shells:
+ * @ui_configurer:
+ * @source_image_window:
+ * @target_image_window:
+ *
+ * Move all display shells from one image window to the another.
+ **/
+static void
+gimp_ui_configurer_move_shells (GimpUIConfigurer *ui_configurer,
+ GimpImageWindow *source_image_window,
+ GimpImageWindow *target_image_window)
+{
+ while (gimp_image_window_get_n_shells (source_image_window) > 0)
+ {
+ GimpDisplayShell *shell;
+
+ shell = gimp_image_window_get_shell (source_image_window, 0);
+
+ g_object_ref (shell);
+ gimp_image_window_remove_shell (source_image_window, shell);
+ gimp_image_window_add_shell (target_image_window, shell);
+ g_object_unref (shell);
+ }
+}
+
+/**
+ * gimp_ui_configurer_separate_docks:
+ * @ui_configurer:
+ * @image_window:
+ *
+ * Move out the docks from the image window.
+ **/
+static void
+gimp_ui_configurer_separate_docks (GimpUIConfigurer *ui_configurer,
+ GimpImageWindow *image_window)
+{
+ GimpDockColumns *left_docks = NULL;
+ GimpDockColumns *right_docks = NULL;
+
+ left_docks = gimp_image_window_get_left_docks (image_window);
+ right_docks = gimp_image_window_get_right_docks (image_window);
+
+ gimp_ui_configurer_move_docks_to_window (ui_configurer, left_docks, GIMP_ALIGN_LEFT);
+ gimp_ui_configurer_move_docks_to_window (ui_configurer, right_docks, GIMP_ALIGN_RIGHT);
+}
+
+/**
+ * gimp_ui_configurer_move_docks_to_window:
+ * @dock_columns:
+ * @screen_side: At what side of the screen the dock window should be put.
+ *
+ * Moves docks in @dock_columns into a new #GimpDockWindow and
+ * position it on the screen in a non-overlapping manner.
+ */
+static void
+gimp_ui_configurer_move_docks_to_window (GimpUIConfigurer *ui_configurer,
+ GimpDockColumns *dock_columns,
+ GimpAlignmentType screen_side)
+{
+ GdkScreen *screen;
+ gint monitor;
+ GdkRectangle monitor_rect;
+ GList *docks;
+ GList *iter;
+ gboolean contains_toolbox = FALSE;
+ GtkWidget *dock_window;
+ GtkAllocation original_size;
+ gchar geometry[32];
+
+ docks = g_list_copy (gimp_dock_columns_get_docks (dock_columns));
+ if (! docks)
+ return;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (dock_columns));
+ monitor = gimp_widget_get_monitor (GTK_WIDGET (dock_columns));
+
+ gdk_screen_get_monitor_workarea (screen, monitor, &monitor_rect);
+
+ /* Remember the size so we can set the new dock window to the same
+ * size
+ */
+ gtk_widget_get_allocation (GTK_WIDGET (dock_columns), &original_size);
+
+ /* Do we need a toolbox window? */
+ for (iter = docks; iter; iter = g_list_next (iter))
+ {
+ GimpDock *dock = GIMP_DOCK (iter->data);
+
+ if (GIMP_IS_TOOLBOX (dock))
+ {
+ contains_toolbox = TRUE;
+ break;
+ }
+ }
+
+ /* Create a dock window to put the dock in. Checking for
+ * GIMP_IS_TOOLBOX() is kind of ugly but not a disaster. We need
+ * the dock window correctly configured if we create it for the
+ * toolbox
+ */
+ dock_window =
+ gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (),
+ screen,
+ monitor,
+ NULL /*ui_manager*/,
+ (contains_toolbox ?
+ "gimp-toolbox-window" :
+ "gimp-dock-window"),
+ -1 /*view_size*/,
+ FALSE /*present*/);
+
+ for (iter = docks; iter; iter = g_list_next (iter))
+ {
+ GimpDock *dock = GIMP_DOCK (iter->data);
+
+ /* Move the dock to the window */
+ g_object_ref (dock);
+ gimp_dock_columns_remove_dock (dock_columns, dock);
+ gimp_dock_window_add_dock (GIMP_DOCK_WINDOW (dock_window), dock, -1);
+ g_object_unref (dock);
+ }
+
+ /* Position the window */
+ if (screen_side == GIMP_ALIGN_LEFT)
+ {
+ g_snprintf (geometry, sizeof (geometry), "%+d%+d",
+ monitor_rect.x,
+ monitor_rect.y);
+ }
+ else if (screen_side == GIMP_ALIGN_RIGHT)
+ {
+ g_snprintf (geometry, sizeof (geometry), "%+d%+d",
+ monitor_rect.x + monitor_rect.width - original_size.width,
+ monitor_rect.y);
+ }
+ else
+ {
+ gimp_assert_not_reached ();
+ }
+
+ gtk_window_parse_geometry (GTK_WINDOW (dock_window), geometry);
+
+ /* Try to keep the same size */
+ gtk_window_set_default_size (GTK_WINDOW (dock_window),
+ original_size.width,
+ original_size.height);
+
+ /* Don't forget to show the window */
+ gtk_widget_show (dock_window);
+
+ g_list_free (docks);
+}
+
+/**
+ * gimp_ui_configurer_separate_shells:
+ * @ui_configurer:
+ * @source_image_window:
+ *
+ * Create one image window per display shell and move it there.
+ **/
+static void
+gimp_ui_configurer_separate_shells (GimpUIConfigurer *ui_configurer,
+ GimpImageWindow *source_image_window)
+{
+ GimpDisplayShell *active_shell = gimp_image_window_get_active_shell (source_image_window);
+ GimpImageWindow *active_window = NULL;
+
+ /* The last display shell remains in its window */
+ while (gimp_image_window_get_n_shells (source_image_window) > 1)
+ {
+ GimpImageWindow *new_image_window;
+ GimpDisplayShell *shell;
+
+ /* Create a new image window */
+ new_image_window = gimp_image_window_new (ui_configurer->p->gimp,
+ NULL,
+ gimp_dialog_factory_get_singleton (),
+ gtk_widget_get_screen (GTK_WIDGET (source_image_window)),
+ gimp_widget_get_monitor (GTK_WIDGET (source_image_window)));
+ /* Move the shell there */
+ shell = gimp_image_window_get_shell (source_image_window, 1);
+
+ if (shell == active_shell)
+ active_window = new_image_window;
+
+ g_object_ref (shell);
+ gimp_image_window_remove_shell (source_image_window, shell);
+ gimp_image_window_add_shell (new_image_window, shell);
+ g_object_unref (shell);
+
+ /* FIXME: If we don't set a size request here the window will be
+ * too small. Get rid of this hack and fix it the proper way
+ */
+ gtk_widget_set_size_request (GTK_WIDGET (new_image_window), 640, 480);
+
+ /* Show after we have added the shell */
+ gtk_widget_show (GTK_WIDGET (new_image_window));
+ }
+
+ /* If none of the shells were active, I assume the first one is. */
+ if (active_window == NULL)
+ active_window = source_image_window;
+
+ /* The active tab must stay at the top of the windows stack. */
+ gtk_window_present (GTK_WINDOW (active_window));
+}
+
+/**
+ * gimp_ui_configurer_configure_for_single_window:
+ * @ui_configurer:
+ *
+ * Move docks and display shells into a single window.
+ **/
+static void
+gimp_ui_configurer_configure_for_single_window (GimpUIConfigurer *ui_configurer)
+{
+ Gimp *gimp = ui_configurer->p->gimp;
+ GList *windows = gimp_get_image_windows (gimp);
+ GList *iter = NULL;
+ GimpImageWindow *uber_image_window = NULL;
+ GimpDisplay *active_display = gimp_context_get_display (gimp_get_user_context (gimp));
+ GimpDisplayShell *active_shell = gimp_display_get_shell (active_display);
+
+ /* Get and setup the window to put everything in */
+ uber_image_window = gimp_ui_configurer_get_uber_window (ui_configurer);
+
+ /* Mve docks to the left and right side of the image window */
+ gimp_ui_configurer_move_docks_to_columns (ui_configurer,
+ uber_image_window);
+
+ /* Move image shells from other windows to the uber image window */
+ for (iter = windows; iter; iter = g_list_next (iter))
+ {
+ GimpImageWindow *image_window = GIMP_IMAGE_WINDOW (iter->data);
+
+ /* Don't move stuff to itself */
+ if (image_window == uber_image_window)
+ continue;
+
+ /* Put the displays in the rest of the image windows into
+ * the uber image window
+ */
+ gimp_ui_configurer_move_shells (ui_configurer,
+ image_window,
+ uber_image_window);
+ /* Destroy the window */
+ gimp_image_window_destroy (image_window);
+ }
+
+ /* Ensure the context shell remains active after mode switch. */
+ gimp_image_window_set_active_shell (uber_image_window, active_shell);
+
+ g_list_free (windows);
+}
+
+/**
+ * gimp_ui_configurer_configure_for_multi_window:
+ * @ui_configurer:
+ *
+ * Moves all display shells into their own image window.
+ **/
+static void
+gimp_ui_configurer_configure_for_multi_window (GimpUIConfigurer *ui_configurer)
+{
+ Gimp *gimp = ui_configurer->p->gimp;
+ GList *windows = gimp_get_image_windows (gimp);
+ GList *iter = NULL;
+
+ for (iter = windows; iter; iter = g_list_next (iter))
+ {
+ GimpImageWindow *image_window = GIMP_IMAGE_WINDOW (iter->data);
+
+ gimp_ui_configurer_separate_docks (ui_configurer, image_window);
+
+ gimp_ui_configurer_separate_shells (ui_configurer, image_window);
+ }
+
+ g_list_free (windows);
+}
+
+/**
+ * gimp_ui_configurer_get_uber_window:
+ * @ui_configurer:
+ *
+ * Returns: The window to be used as the main window for single-window
+ * mode.
+ **/
+static GimpImageWindow *
+gimp_ui_configurer_get_uber_window (GimpUIConfigurer *ui_configurer)
+{
+ Gimp *gimp = ui_configurer->p->gimp;
+ GimpDisplay *display = gimp_get_display_iter (gimp)->data;
+ GimpDisplayShell *shell = gimp_display_get_shell (display);
+ GimpImageWindow *image_window = gimp_display_shell_get_window (shell);
+
+ return image_window;
+}
+
+/**
+ * gimp_ui_configurer_update_appearance:
+ * @ui_configurer:
+ *
+ * Updates the appearance of all shells in all image windows, so they
+ * do whatever they deem necessary to fit the new UI mode mode.
+ **/
+static void
+gimp_ui_configurer_update_appearance (GimpUIConfigurer *ui_configurer)
+{
+ Gimp *gimp = ui_configurer->p->gimp;
+ GList *windows = gimp_get_image_windows (gimp);
+ GList *list;
+
+ for (list = windows; list; list = g_list_next (list))
+ {
+ GimpImageWindow *image_window = GIMP_IMAGE_WINDOW (list->data);
+ gint n_shells;
+ gint i;
+
+ n_shells = gimp_image_window_get_n_shells (image_window);
+
+ for (i = 0; i < n_shells; i++)
+ {
+ GimpDisplayShell *shell;
+
+ shell = gimp_image_window_get_shell (image_window, i);
+
+ gimp_display_shell_appearance_update (shell);
+ }
+ }
+
+ g_list_free (windows);
+}
+
+/**
+ * gimp_ui_configurer_configure:
+ * @ui_configurer:
+ * @single_window_mode:
+ *
+ * Configure the UI.
+ **/
+void
+gimp_ui_configurer_configure (GimpUIConfigurer *ui_configurer,
+ gboolean single_window_mode)
+{
+ if (single_window_mode)
+ gimp_ui_configurer_configure_for_single_window (ui_configurer);
+ else
+ gimp_ui_configurer_configure_for_multi_window (ui_configurer);
+
+ gimp_ui_configurer_update_appearance (ui_configurer);
+}
diff --git a/app/gui/gimpuiconfigurer.h b/app/gui/gimpuiconfigurer.h
new file mode 100644
index 0000000..db22ffc
--- /dev/null
+++ b/app/gui/gimpuiconfigurer.h
@@ -0,0 +1,57 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpuiconfigurer.h
+ * Copyright (C) 2009 Martin Nordholts <martinn@src.gnome.org>
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_UI_CONFIGURER_H__
+#define __GIMP_UI_CONFIGURER_H__
+
+
+#include "core/gimpobject.h"
+
+
+#define GIMP_TYPE_UI_CONFIGURER (gimp_ui_configurer_get_type ())
+#define GIMP_UI_CONFIGURER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_UI_CONFIGURER, GimpUIConfigurer))
+#define GIMP_UI_CONFIGURER_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GIMP_TYPE_UI_CONFIGURER, GimpUIConfigurerClass))
+#define GIMP_IS_UI_CONFIGURER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_UI_CONFIGURER))
+#define GIMP_IS_UI_CONFIGURER_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GIMP_TYPE_UI_CONFIGURER))
+#define GIMP_UI_CONFIGURER_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GIMP_TYPE_UI_CONFIGURER, GimpUIConfigurerClass))
+
+
+typedef struct _GimpUIConfigurerClass GimpUIConfigurerClass;
+typedef struct _GimpUIConfigurerPrivate GimpUIConfigurerPrivate;
+
+struct _GimpUIConfigurer
+{
+ GimpObject parent_instance;
+
+ GimpUIConfigurerPrivate *p;
+};
+
+struct _GimpUIConfigurerClass
+{
+ GimpObjectClass parent_class;
+};
+
+
+GType gimp_ui_configurer_get_type (void) G_GNUC_CONST;
+void gimp_ui_configurer_configure (GimpUIConfigurer *ui_configurer,
+ gboolean single_window_mode);
+
+
+#endif /* __GIMP_UI_CONFIGURER_H__ */
diff --git a/app/gui/gui-message.c b/app/gui/gui-message.c
new file mode 100644
index 0000000..90e6314
--- /dev/null
+++ b/app/gui/gui-message.c
@@ -0,0 +1,501 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gui-types.h"
+
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimpprogress.h"
+
+#include "plug-in/gimpplugin.h"
+
+#include "widgets/gimpcriticaldialog.h"
+#include "widgets/gimpdialogfactory.h"
+#include "widgets/gimpdockable.h"
+#include "widgets/gimperrorconsole.h"
+#include "widgets/gimperrordialog.h"
+#include "widgets/gimpprogressdialog.h"
+#include "widgets/gimpsessioninfo.h"
+#include "widgets/gimpwidgets-utils.h"
+#include "widgets/gimpwindowstrategy.h"
+
+#include "about.h"
+
+#include "gui-message.h"
+
+#include "gimp-intl.h"
+
+
+#define MAX_TRACES 3
+#define MAX_ERRORS 10
+
+
+typedef struct
+{
+ Gimp *gimp;
+ gchar *domain;
+ gchar *message;
+ gchar *trace;
+ GObject *handler;
+ GimpMessageSeverity severity;
+} GimpLogMessageData;
+
+
+static gboolean gui_message_idle (gpointer user_data);
+static gboolean gui_message_error_console (Gimp *gimp,
+ GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message);
+static gboolean gui_message_error_dialog (Gimp *gimp,
+ GObject *handler,
+ GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message,
+ const gchar *trace);
+static void gui_message_console (GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message);
+static gchar * gui_message_format (GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message);
+
+static GtkWidget * global_error_dialog (void);
+static GtkWidget * global_critical_dialog (void);
+
+static void gui_message_reset_errors (GObject *object,
+ gpointer user_data);
+
+
+static GMutex mutex;
+static gint n_traces = 0;
+static gint n_errors = 0;
+
+
+void
+gui_message (Gimp *gimp,
+ GObject *handler,
+ GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message)
+{
+ gchar *trace = NULL;
+ gboolean gen_trace = FALSE;
+
+ switch (gimp->message_handler)
+ {
+ case GIMP_ERROR_CONSOLE:
+ if (gui_message_error_console (gimp, severity, domain, message))
+ return;
+
+ gimp->message_handler = GIMP_MESSAGE_BOX;
+ /* fallthru */
+
+ case GIMP_MESSAGE_BOX:
+ if (severity >= GIMP_MESSAGE_BUG_WARNING)
+ {
+ g_mutex_lock (&mutex);
+ /* Trace creation can be time consuming so don't block the
+ * mutex for too long and only increment and set a boolean
+ * here.
+ */
+ if (n_traces < MAX_TRACES)
+ {
+ gen_trace = TRUE;
+ n_traces++;
+ }
+ g_mutex_unlock (&mutex);
+ }
+
+ if (gen_trace)
+ {
+ /* We need to create the trace here because for multi-thread
+ * errors (i.e. non-GIMP ones), the backtrace after a idle
+ * function will simply be useless. It needs to happen in the
+ * buggy thread to be meaningful.
+ */
+ gimp_stack_trace_print (NULL, NULL, &trace);
+ }
+
+ if (g_strcmp0 (GIMP_ACRONYM, domain) != 0)
+ {
+ /* Handle non-GIMP messages in a multi-thread safe way,
+ * because we can't know for sure whether the log message may
+ * not have been called from a thread other than the main one.
+ */
+ GimpLogMessageData *data;
+
+ data = g_new0 (GimpLogMessageData, 1);
+ data->gimp = gimp;
+ data->domain = g_strdup (domain);
+ data->message = g_strdup (message);
+ data->trace = trace;
+ data->handler = handler? g_object_ref (handler) : NULL;
+ data->severity = severity;
+
+ gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE,
+ gui_message_idle,
+ data, g_free);
+ return;
+ }
+ if (gui_message_error_dialog (gimp, handler, severity,
+ domain, message, trace))
+ break;
+
+ gimp->message_handler = GIMP_CONSOLE;
+ /* fallthru */
+
+ case GIMP_CONSOLE:
+ gui_message_console (severity, domain, message);
+ break;
+ }
+ if (trace)
+ g_free (trace);
+}
+
+static gboolean
+gui_message_idle (gpointer user_data)
+{
+ GimpLogMessageData *data = (GimpLogMessageData *) user_data;
+
+ if (! gui_message_error_dialog (data->gimp,
+ data->handler,
+ data->severity,
+ data->domain,
+ data->message,
+ data->trace))
+ {
+ gui_message_console (data->severity,
+ data->domain,
+ data->message);
+ }
+ g_free (data->domain);
+ g_free (data->message);
+ if (data->trace)
+ g_free (data->trace);
+ if (data->handler)
+ g_object_unref (data->handler);
+
+ return FALSE;
+}
+
+static gboolean
+gui_message_error_console (Gimp *gimp,
+ GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message)
+{
+ GtkWidget *dockable;
+
+ dockable = gimp_dialog_factory_find_widget (gimp_dialog_factory_get_singleton (),
+ "gimp-error-console");
+
+ /* avoid raising the error console for unhighlighted messages */
+ if (dockable)
+ {
+ GtkWidget *child = gtk_bin_get_child (GTK_BIN (dockable));
+
+ if (GIMP_ERROR_CONSOLE (child)->highlight[severity])
+ dockable = NULL;
+ }
+
+ if (! dockable)
+ {
+ GdkScreen *screen;
+ gint monitor;
+
+ monitor = gimp_get_monitor_at_pointer (&screen);
+
+ dockable =
+ gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)),
+ gimp,
+ gimp_dialog_factory_get_singleton (),
+ screen, monitor,
+ "gimp-error-console");
+ }
+
+ if (dockable)
+ {
+ GtkWidget *child = gtk_bin_get_child (GTK_BIN (dockable));
+
+ gimp_error_console_add (GIMP_ERROR_CONSOLE (child),
+ severity, domain, message);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+progress_error_dialog_unset (GimpProgress *progress)
+{
+ g_object_set_data (G_OBJECT (progress), "gimp-error-dialog", NULL);
+}
+
+static GtkWidget *
+progress_error_dialog (GimpProgress *progress)
+{
+ GtkWidget *dialog;
+
+ g_return_val_if_fail (GIMP_IS_PROGRESS (progress), NULL);
+
+ dialog = g_object_get_data (G_OBJECT (progress), "gimp-error-dialog");
+
+ if (! dialog)
+ {
+ dialog = gimp_error_dialog_new (_("GIMP Message"));
+
+ g_object_set_data (G_OBJECT (progress), "gimp-error-dialog", dialog);
+
+ g_signal_connect_object (dialog, "destroy",
+ G_CALLBACK (progress_error_dialog_unset),
+ progress, G_CONNECT_SWAPPED);
+
+ if (GTK_IS_WIDGET (progress))
+ {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (progress));
+
+ if (GTK_IS_WINDOW (toplevel))
+ gtk_window_set_transient_for (GTK_WINDOW (dialog),
+ GTK_WINDOW (toplevel));
+ }
+ else
+ {
+ guint32 window_id = gimp_progress_get_window_id (progress);
+
+ if (window_id)
+ gimp_window_set_transient_for (GTK_WINDOW (dialog), window_id);
+ }
+ }
+
+ return dialog;
+}
+
+static gboolean
+gui_message_error_dialog (Gimp *gimp,
+ GObject *handler,
+ GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message,
+ const gchar *trace)
+{
+ GtkWidget *dialog;
+ GtkMessageType type = GTK_MESSAGE_ERROR;
+
+ switch (severity)
+ {
+ case GIMP_MESSAGE_INFO:
+ type = GTK_MESSAGE_INFO;
+ break;
+ case GIMP_MESSAGE_WARNING:
+ type = GTK_MESSAGE_WARNING;
+ break;
+ case GIMP_MESSAGE_ERROR:
+ type = GTK_MESSAGE_ERROR;
+ break;
+ case GIMP_MESSAGE_BUG_WARNING:
+ case GIMP_MESSAGE_BUG_CRITICAL:
+ type = GTK_MESSAGE_OTHER;
+ break;
+ }
+
+ if (severity >= GIMP_MESSAGE_BUG_WARNING)
+ {
+ /* Process differently programming errors.
+ * The reason is that we will generate traces, which will take
+ * significant place, and cannot be processed as a progress
+ * message or in the global dialog. It will require its own
+ * dedicated dialog which will encourage people to report the bug.
+ */
+ gboolean gui_error = FALSE;
+
+ g_mutex_lock (&mutex);
+ if (n_errors < MAX_ERRORS)
+ {
+ gui_error = TRUE;
+ n_errors++;
+ }
+ g_mutex_unlock (&mutex);
+
+ if (gui_error || trace)
+ {
+ gchar *text;
+
+ dialog = global_critical_dialog ();
+
+ text = gui_message_format (severity, domain, message);
+ gimp_critical_dialog_add (dialog, text, trace, FALSE, NULL, 0);
+
+ gtk_widget_show (dialog);
+
+ g_free (text);
+
+ return TRUE;
+ }
+ else
+ {
+ const gchar *reason = "Message";
+
+ gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity,
+ NULL, NULL, &reason, NULL);
+
+ /* Since we overridden glib default's WARNING and CRITICAL
+ * handler, if we decide not to handle this error in the end,
+ * let's just print it in terminal in a similar fashion as
+ * glib's default handler (though without the fancy terminal
+ * colors right now).
+ */
+ g_printerr ("%s-%s: %s\n", domain, reason, message);
+
+ return TRUE;
+ }
+ }
+ else if (GIMP_IS_PROGRESS (handler))
+ {
+ /* If there's already an error dialog associated with this
+ * progress, then continue without trying gimp_progress_message().
+ */
+ if (! g_object_get_data (handler, "gimp-error-dialog") &&
+ gimp_progress_message (GIMP_PROGRESS (handler), gimp,
+ severity, domain, message))
+ {
+ return TRUE;
+ }
+ }
+ else if (GTK_IS_WIDGET (handler))
+ {
+ GtkWidget *parent = GTK_WIDGET (handler);
+
+ dialog =
+ gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (parent)),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ type, GTK_BUTTONS_OK,
+ "%s", message);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+
+ gtk_widget_show (dialog);
+
+ return TRUE;
+ }
+
+ if (GIMP_IS_PROGRESS (handler) && ! GIMP_IS_PROGRESS_DIALOG (handler))
+ dialog = progress_error_dialog (GIMP_PROGRESS (handler));
+ else
+ dialog = global_error_dialog ();
+
+ if (dialog)
+ {
+ gimp_error_dialog_add (GIMP_ERROR_DIALOG (dialog),
+ gimp_get_message_icon_name (severity),
+ domain, message);
+ gtk_window_present (GTK_WINDOW (dialog));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gui_message_console (GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message)
+{
+ gchar *formatted_message;
+
+ formatted_message = gui_message_format (severity, domain, message);
+ g_printerr ("%s\n\n", formatted_message);
+ g_free (formatted_message);
+}
+
+static gchar *
+gui_message_format (GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message)
+{
+ const gchar *desc = "Message";
+ gchar *formatted_message;
+
+ gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity,
+ NULL, NULL, &desc, NULL);
+
+ formatted_message = g_strdup_printf ("%s-%s: %s", domain, desc, message);
+
+ return formatted_message;
+}
+
+static GtkWidget *
+global_error_dialog (void)
+{
+ GdkScreen *screen;
+ gint monitor;
+
+ monitor = gimp_get_monitor_at_pointer (&screen);
+
+ return gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (),
+ screen, monitor,
+ NULL /*ui_manager*/,
+ "gimp-error-dialog", -1,
+ FALSE);
+}
+
+static GtkWidget *
+global_critical_dialog (void)
+{
+ GtkWidget *dialog;
+ GdkScreen *screen;
+ gint monitor;
+
+ monitor = gimp_get_monitor_at_pointer (&screen);
+
+ dialog = gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (),
+ screen, monitor,
+ NULL /*ui_manager*/,
+ "gimp-critical-dialog", -1,
+ FALSE);
+ g_signal_handlers_disconnect_by_func (dialog,
+ gui_message_reset_errors,
+ NULL);
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gui_message_reset_errors),
+ NULL);
+ return dialog;
+}
+
+static void
+gui_message_reset_errors (GObject *object,
+ gpointer user_data)
+{
+ g_mutex_lock (&mutex);
+ n_errors = 0;
+ n_traces = 0;
+ g_mutex_unlock (&mutex);
+}
diff --git a/app/gui/gui-message.h b/app/gui/gui-message.h
new file mode 100644
index 0000000..e866f2a
--- /dev/null
+++ b/app/gui/gui-message.h
@@ -0,0 +1,29 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GUI_MESSAGE_H__
+#define __GUI_MESSAGE_H__
+
+
+void gui_message (Gimp *gimp,
+ GObject *handler,
+ GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message);
+
+
+#endif /* __GUI_VTABLE_H__ */
diff --git a/app/gui/gui-types.h b/app/gui/gui-types.h
new file mode 100644
index 0000000..82e9f20
--- /dev/null
+++ b/app/gui/gui-types.h
@@ -0,0 +1,27 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GUI_TYPES_H__
+#define __GUI_TYPES_H__
+
+
+#include "tools/tools-types.h"
+#include "dialogs/dialogs-types.h"
+#include "menus/menus-types.h"
+
+
+#endif /* __GUI_TYPES_H__ */
diff --git a/app/gui/gui-unique.c b/app/gui/gui-unique.c
new file mode 100644
index 0000000..c54a05b
--- /dev/null
+++ b/app/gui/gui-unique.c
@@ -0,0 +1,442 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#endif
+
+#ifdef GDK_WINDOWING_QUARTZ
+#import <AppKit/AppKit.h>
+#include <gtkosxapplication.h>
+#endif
+
+#include "gui/gui-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpcontainer.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimpdisplayshell.h"
+#include "display/gimpimagewindow.h"
+
+#include "file/file-open.h"
+
+#include "gimpdbusservice.h"
+#include "gui-unique.h"
+
+
+#ifdef G_OS_WIN32
+
+static void gui_unique_win32_init (Gimp *gimp);
+static void gui_unique_win32_exit (void);
+
+static Gimp *unique_gimp = NULL;
+static HWND proxy_window = NULL;
+
+#elif defined (GDK_WINDOWING_QUARTZ)
+
+static void gui_unique_quartz_init (Gimp *gimp);
+static void gui_unique_quartz_exit (void);
+
+@interface GimpAppleEventHandler : NSObject {}
+- (void) handleEvent:(NSAppleEventDescriptor *) inEvent
+ andReplyWith:(NSAppleEventDescriptor *) replyEvent;
+@end
+
+static Gimp *unique_gimp = NULL;
+static GimpAppleEventHandler *event_handler = NULL;
+
+#else
+
+static void gui_dbus_service_init (Gimp *gimp);
+static void gui_dbus_service_exit (void);
+
+static GDBusObjectManagerServer *dbus_manager = NULL;
+static guint dbus_name_id = 0;
+
+#endif
+
+
+void
+gui_unique_init (Gimp *gimp)
+{
+#ifdef G_OS_WIN32
+ gui_unique_win32_init (gimp);
+#elif defined (GDK_WINDOWING_QUARTZ)
+ gui_unique_quartz_init (gimp);
+#else
+ gui_dbus_service_init (gimp);
+#endif
+}
+
+void
+gui_unique_exit (void)
+{
+#ifdef G_OS_WIN32
+ gui_unique_win32_exit ();
+#elif defined (GDK_WINDOWING_QUARTZ)
+ gui_unique_quartz_exit ();
+#else
+ gui_dbus_service_exit ();
+#endif
+}
+
+
+#ifdef G_OS_WIN32
+
+typedef struct
+{
+ GFile *file;
+ gboolean as_new;
+} IdleOpenData;
+
+static IdleOpenData *
+idle_open_data_new (GFile *file,
+ gboolean as_new)
+{
+ IdleOpenData *data = g_slice_new0 (IdleOpenData);
+
+ data->file = g_object_ref (file);
+ data->as_new = as_new;
+
+ return data;
+}
+
+static void
+idle_open_data_free (IdleOpenData *data)
+{
+ g_object_unref (data->file);
+ g_slice_free (IdleOpenData, data);
+}
+
+static gboolean
+gui_unique_win32_idle_open (IdleOpenData *data)
+{
+ /* We want to be called again later in case that GIMP is not fully
+ * started yet.
+ */
+ if (! gimp_is_restored (unique_gimp))
+ return TRUE;
+
+ if (data->file)
+ {
+ file_open_from_command_line (unique_gimp, data->file,
+ data->as_new, NULL, 0);
+ }
+ else
+ {
+ /* raise the first display */
+ GimpObject *display;
+
+ display = gimp_container_get_first_child (unique_gimp->displays);
+
+ gimp_display_shell_present (gimp_display_get_shell (GIMP_DISPLAY (display)));
+ }
+
+ return FALSE;
+}
+
+static LRESULT CALLBACK
+gui_unique_win32_message_handler (HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_COPYDATA:
+ if (unique_gimp)
+ {
+ COPYDATASTRUCT *copydata = (COPYDATASTRUCT *) lParam;
+ GimpObject *display;
+
+ if (copydata->cbData > 0)
+ {
+ GSource *source;
+ GClosure *closure;
+ GFile *file;
+ IdleOpenData *data;
+
+ file = g_file_new_for_uri (copydata->lpData);
+
+ data = idle_open_data_new (file,
+ copydata->dwData != 0);
+
+ g_object_unref (file);
+
+ closure = g_cclosure_new (G_CALLBACK (gui_unique_win32_idle_open),
+ data,
+ (GClosureNotify) idle_open_data_free);
+
+ g_object_watch_closure (G_OBJECT (unique_gimp), closure);
+
+ source = g_idle_source_new ();
+ g_source_set_priority (source, G_PRIORITY_LOW);
+ g_source_set_closure (source, closure);
+ g_source_attach (source, NULL);
+ g_source_unref (source);
+ }
+
+ /* Deiconify the window if minimized. */
+ display = gimp_container_get_first_child (unique_gimp->displays);
+ if (display)
+ gimp_display_shell_present (gimp_display_get_shell (GIMP_DISPLAY (display)));
+ }
+ return TRUE;
+
+ default:
+ return DefWindowProcW (hWnd, uMsg, wParam, lParam);
+ }
+}
+
+static void
+gui_unique_win32_init (Gimp *gimp)
+{
+ WNDCLASSW wc;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+ g_return_if_fail (unique_gimp == NULL);
+
+ unique_gimp = gimp;
+
+ /* register window class for proxy window */
+ memset (&wc, 0, sizeof (wc));
+
+ wc.hInstance = GetModuleHandle (NULL);
+ wc.lpfnWndProc = gui_unique_win32_message_handler;
+ wc.lpszClassName = GIMP_UNIQUE_WIN32_WINDOW_CLASS;
+
+ RegisterClassW (&wc);
+
+ proxy_window = CreateWindowExW (0,
+ GIMP_UNIQUE_WIN32_WINDOW_CLASS,
+ GIMP_UNIQUE_WIN32_WINDOW_NAME,
+ WS_POPUP, 0, 0, 1, 1, NULL, NULL, wc.hInstance, NULL);
+}
+
+static void
+gui_unique_win32_exit (void)
+{
+ g_return_if_fail (GIMP_IS_GIMP (unique_gimp));
+
+ unique_gimp = NULL;
+
+ DestroyWindow (proxy_window);
+}
+
+#elif defined (GDK_WINDOWING_QUARTZ)
+
+static gboolean
+gui_unique_quartz_idle_open (GFile *file)
+{
+ /* We want to be called again later in case that GIMP is not fully
+ * started yet.
+ */
+ if (! gimp_is_restored (unique_gimp))
+ return TRUE;
+
+ if (file)
+ {
+ file_open_from_command_line (unique_gimp, file, FALSE, NULL, 0);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gui_unique_quartz_nsopen_file_callback (GtkosxApplication *osx_app,
+ gchar *path,
+ gpointer user_data)
+{
+ GSource *source;
+ GClosure *closure;
+
+ closure = g_cclosure_new (G_CALLBACK (gui_unique_quartz_idle_open),
+ g_file_new_for_path (path),
+ (GClosureNotify) g_object_unref);
+
+ g_object_watch_closure (G_OBJECT (unique_gimp), closure);
+
+ source = g_idle_source_new ();
+
+ g_source_set_priority (source, G_PRIORITY_LOW);
+ g_source_set_closure (source, closure);
+ g_source_attach (source, NULL);
+ g_source_unref (source);
+
+ return TRUE;
+}
+
+@implementation GimpAppleEventHandler
+- (void) handleEvent: (NSAppleEventDescriptor *) inEvent
+ andReplyWith: (NSAppleEventDescriptor *) replyEvent
+{
+ NSAutoreleasePool *urlpool;
+ NSInteger count;
+ NSInteger i;
+
+ urlpool = [[NSAutoreleasePool alloc] init];
+
+ count = [inEvent numberOfItems];
+
+ for (i = 1; i <= count; i++)
+ {
+ NSURL *url;
+ const gchar *path;
+ GSource *source;
+ GClosure *closure;
+
+ url = [NSURL URLWithString: [[inEvent descriptorAtIndex: i] stringValue]];
+ path = [[url path] UTF8String];
+
+ closure = g_cclosure_new (G_CALLBACK (gui_unique_quartz_idle_open),
+ g_file_new_for_path (path),
+ (GClosureNotify) g_object_unref);
+
+ g_object_watch_closure (G_OBJECT (unique_gimp), closure);
+
+ source = g_idle_source_new ();
+ g_source_set_priority (source, G_PRIORITY_LOW);
+ g_source_set_closure (source, closure);
+ g_source_attach (source, NULL);
+ g_source_unref (source);
+ }
+
+ [urlpool drain];
+}
+@end
+
+static void
+gui_unique_quartz_init (Gimp *gimp)
+{
+ GtkosxApplication *osx_app;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+ g_return_if_fail (unique_gimp == NULL);
+
+ osx_app = gtkosx_application_get ();
+
+ unique_gimp = gimp;
+
+ g_signal_connect (osx_app, "NSApplicationOpenFile",
+ G_CALLBACK (gui_unique_quartz_nsopen_file_callback),
+ gimp);
+
+ /* Using the event handler is a hack, it is necessary because
+ * gtkosx_application will drop the file open events if any
+ * event processing is done before gtkosx_application_ready is
+ * called, which we unfortuantly can't avoid doing right now.
+ */
+ event_handler = [[GimpAppleEventHandler alloc] init];
+
+ [[NSAppleEventManager sharedAppleEventManager]
+ setEventHandler: event_handler
+ andSelector: @selector (handleEvent: andReplyWith:)
+ forEventClass: kCoreEventClass
+ andEventID: kAEOpenDocuments];
+}
+
+static void
+gui_unique_quartz_exit (void)
+{
+ g_return_if_fail (GIMP_IS_GIMP (unique_gimp));
+
+ unique_gimp = NULL;
+
+ [[NSAppleEventManager sharedAppleEventManager]
+ removeEventHandlerForEventClass: kCoreEventClass
+ andEventID: kAEOpenDocuments];
+
+ [event_handler release];
+
+ event_handler = NULL;
+}
+
+#else
+
+static void
+gui_dbus_bus_acquired (GDBusConnection *connection,
+ const gchar *name,
+ Gimp *gimp)
+{
+ GDBusObjectSkeleton *object;
+ GObject *service;
+
+ /* this should use GIMP_DBUS_SERVICE_PATH, but that's historically wrong */
+ dbus_manager = g_dbus_object_manager_server_new ("/org/gimp/GIMP");
+
+ object = g_dbus_object_skeleton_new (GIMP_DBUS_INTERFACE_PATH);
+
+ service = gimp_dbus_service_new (gimp);
+ g_dbus_object_skeleton_add_interface (object,
+ G_DBUS_INTERFACE_SKELETON (service));
+ g_object_unref (service);
+
+ g_dbus_object_manager_server_export (dbus_manager, object);
+ g_object_unref (object);
+
+ g_dbus_object_manager_server_set_connection (dbus_manager, connection);
+}
+
+static void
+gui_dbus_name_acquired (GDBusConnection *connection,
+ const gchar *name,
+ Gimp *gimp)
+{
+}
+
+static void
+gui_dbus_name_lost (GDBusConnection *connection,
+ const gchar *name,
+ Gimp *gimp)
+{
+ if (connection == NULL)
+ g_printerr ("%s: connection to the bus cannot be established.\n",
+ G_STRFUNC);
+ else
+ g_printerr ("%s: the name \"%s\" could not be acquired on the bus.\n",
+ G_STRFUNC, name);
+}
+
+static void
+gui_dbus_service_init (Gimp *gimp)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+ g_return_if_fail (dbus_name_id == 0);
+
+ dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ GIMP_DBUS_SERVICE_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ (GBusAcquiredCallback) gui_dbus_bus_acquired,
+ (GBusNameAcquiredCallback) gui_dbus_name_acquired,
+ (GBusNameLostCallback) gui_dbus_name_lost,
+ gimp, NULL);
+}
+
+static void
+gui_dbus_service_exit (void)
+{
+ g_bus_unown_name (dbus_name_id);
+ g_clear_object (&dbus_manager);
+}
+
+#endif
diff --git a/app/gui/gui-unique.h b/app/gui/gui-unique.h
new file mode 100644
index 0000000..73b9231
--- /dev/null
+++ b/app/gui/gui-unique.h
@@ -0,0 +1,31 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GUI_UNIQUE_H__
+#define __GUI_UNIQUE_H__
+
+#ifdef G_OS_WIN32
+#define GIMP_UNIQUE_WIN32_WINDOW_CLASS L"GimpWin32UniqueHandler"
+#define GIMP_UNIQUE_WIN32_WINDOW_NAME L"GimpProxy"
+#endif
+
+
+void gui_unique_init (Gimp *gimp);
+void gui_unique_exit (void);
+
+
+#endif /* __GUI_UNIQUE_H__ */
diff --git a/app/gui/gui-vtable.c b/app/gui/gui-vtable.c
new file mode 100644
index 0000000..28c999a
--- /dev/null
+++ b/app/gui/gui-vtable.c
@@ -0,0 +1,934 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#include <fcntl.h>
+#include <io.h>
+
+#ifndef pipe
+#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
+#endif
+#else
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#endif
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gui-types.h"
+
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimp-parallel.h"
+#include "core/gimp-spawn.h"
+#include "core/gimp-utils.h"
+#include "core/gimpasync.h"
+#include "core/gimpbrush.h"
+#include "core/gimpcancelable.h"
+#include "core/gimpcontainer.h"
+#include "core/gimpcontext.h"
+#include "core/gimpgradient.h"
+#include "core/gimpimage.h"
+#include "core/gimpimagefile.h"
+#include "core/gimplist.h"
+#include "core/gimppalette.h"
+#include "core/gimppattern.h"
+#include "core/gimpprogress.h"
+#include "core/gimpwaitable.h"
+
+#include "text/gimpfont.h"
+
+#include "pdb/gimppdb.h"
+#include "pdb/gimpprocedure.h"
+
+#include "plug-in/gimppluginmanager-file.h"
+
+#include "widgets/gimpactiongroup.h"
+#include "widgets/gimpbrushselect.h"
+#include "widgets/gimpdialogfactory.h"
+#include "widgets/gimpdocked.h"
+#include "widgets/gimpfontselect.h"
+#include "widgets/gimpgradientselect.h"
+#include "widgets/gimphelp.h"
+#include "widgets/gimphelp-ids.h"
+#include "widgets/gimpmenufactory.h"
+#include "widgets/gimppaletteselect.h"
+#include "widgets/gimppatternselect.h"
+#include "widgets/gimpprogressdialog.h"
+#include "widgets/gimpuimanager.h"
+#include "widgets/gimpwidgets-utils.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimpdisplay-foreach.h"
+#include "display/gimpdisplayshell.h"
+#include "display/gimpsinglewindowstrategy.h"
+#include "display/gimpmultiwindowstrategy.h"
+
+#include "actions/plug-in-actions.h"
+
+#include "menus/menus.h"
+
+#include "dialogs/color-profile-import-dialog.h"
+
+#include "gui.h"
+#include "gui-message.h"
+#include "gui-vtable.h"
+#include "icon-themes.h"
+#include "themes.h"
+
+
+/* local function prototypes */
+
+static void gui_ungrab (Gimp *gimp);
+
+static void gui_threads_enter (Gimp *gimp);
+static void gui_threads_leave (Gimp *gimp);
+
+static void gui_set_busy (Gimp *gimp);
+static void gui_unset_busy (Gimp *gimp);
+
+static void gui_help (Gimp *gimp,
+ GimpProgress *progress,
+ const gchar *help_domain,
+ const gchar *help_id);
+static const gchar * gui_get_program_class (Gimp *gimp);
+static gchar * gui_get_display_name (Gimp *gimp,
+ gint display_ID,
+ GObject **screen,
+ gint *monitor);
+static guint32 gui_get_user_time (Gimp *gimp);
+static GFile * gui_get_theme_dir (Gimp *gimp);
+static GFile * gui_get_icon_theme_dir (Gimp *gimp);
+static GimpObject * gui_get_window_strategy (Gimp *gimp);
+static GimpObject * gui_get_empty_display (Gimp *gimp);
+static GimpObject * gui_display_get_by_ID (Gimp *gimp,
+ gint ID);
+static gint gui_display_get_ID (GimpObject *display);
+static guint32 gui_display_get_window_id (GimpObject *display);
+static GimpObject * gui_display_create (Gimp *gimp,
+ GimpImage *image,
+ GimpUnit unit,
+ gdouble scale,
+ GObject *screen,
+ gint monitor);
+static void gui_display_delete (GimpObject *display);
+static void gui_displays_reconnect (Gimp *gimp,
+ GimpImage *old_image,
+ GimpImage *new_image);
+static gboolean gui_wait (Gimp *gimp,
+ GimpWaitable *waitable,
+ const gchar *message);
+static GimpProgress * gui_new_progress (Gimp *gimp,
+ GimpObject *display);
+static void gui_free_progress (Gimp *gimp,
+ GimpProgress *progress);
+static gboolean gui_pdb_dialog_new (Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpContainer *container,
+ const gchar *title,
+ const gchar *callback_name,
+ const gchar *object_name,
+ va_list args);
+static gboolean gui_pdb_dialog_set (Gimp *gimp,
+ GimpContainer *container,
+ const gchar *callback_name,
+ const gchar *object_name,
+ va_list args);
+static gboolean gui_pdb_dialog_close (Gimp *gimp,
+ GimpContainer *container,
+ const gchar *callback_name);
+static gboolean gui_recent_list_add_file (Gimp *gimp,
+ GFile *file,
+ const gchar *mime_type);
+static void gui_recent_list_load (Gimp *gimp);
+
+static GMountOperation
+ * gui_get_mount_operation (Gimp *gimp,
+ GimpProgress *progress);
+
+static GimpColorProfilePolicy
+ gui_query_profile_policy (Gimp *gimp,
+ GimpImage *image,
+ GimpContext *context,
+ GimpColorProfile **dest_profile,
+ GimpColorRenderingIntent *intent,
+ gboolean *bpc,
+ gboolean *dont_ask);
+
+
+/* public functions */
+
+void
+gui_vtable_init (Gimp *gimp)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ gimp->gui.ungrab = gui_ungrab;
+ gimp->gui.threads_enter = gui_threads_enter;
+ gimp->gui.threads_leave = gui_threads_leave;
+ gimp->gui.set_busy = gui_set_busy;
+ gimp->gui.unset_busy = gui_unset_busy;
+ gimp->gui.show_message = gui_message;
+ gimp->gui.help = gui_help;
+ gimp->gui.get_program_class = gui_get_program_class;
+ gimp->gui.get_display_name = gui_get_display_name;
+ gimp->gui.get_user_time = gui_get_user_time;
+ gimp->gui.get_theme_dir = gui_get_theme_dir;
+ gimp->gui.get_icon_theme_dir = gui_get_icon_theme_dir;
+ gimp->gui.get_window_strategy = gui_get_window_strategy;
+ gimp->gui.get_empty_display = gui_get_empty_display;
+ gimp->gui.display_get_by_id = gui_display_get_by_ID;
+ gimp->gui.display_get_id = gui_display_get_ID;
+ gimp->gui.display_get_window_id = gui_display_get_window_id;
+ gimp->gui.display_create = gui_display_create;
+ gimp->gui.display_delete = gui_display_delete;
+ gimp->gui.displays_reconnect = gui_displays_reconnect;
+ gimp->gui.wait = gui_wait;
+ gimp->gui.progress_new = gui_new_progress;
+ gimp->gui.progress_free = gui_free_progress;
+ gimp->gui.pdb_dialog_new = gui_pdb_dialog_new;
+ gimp->gui.pdb_dialog_set = gui_pdb_dialog_set;
+ gimp->gui.pdb_dialog_close = gui_pdb_dialog_close;
+ gimp->gui.recent_list_add_file = gui_recent_list_add_file;
+ gimp->gui.recent_list_load = gui_recent_list_load;
+ gimp->gui.get_mount_operation = gui_get_mount_operation;
+ gimp->gui.query_profile_policy = gui_query_profile_policy;
+}
+
+
+/* private functions */
+
+static void
+gui_ungrab (Gimp *gimp)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+
+ if (display)
+ {
+ gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+ gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
+ }
+}
+
+static void
+gui_threads_enter (Gimp *gimp)
+{
+ GDK_THREADS_ENTER ();
+}
+
+static void
+gui_threads_leave (Gimp *gimp)
+{
+ GDK_THREADS_LEAVE ();
+}
+
+static void
+gui_set_busy (Gimp *gimp)
+{
+ gimp_displays_set_busy (gimp);
+ gimp_dialog_factory_set_busy (gimp_dialog_factory_get_singleton ());
+
+ gdk_flush ();
+}
+
+static void
+gui_unset_busy (Gimp *gimp)
+{
+ gimp_displays_unset_busy (gimp);
+ gimp_dialog_factory_unset_busy (gimp_dialog_factory_get_singleton ());
+
+ gdk_flush ();
+}
+
+static void
+gui_help (Gimp *gimp,
+ GimpProgress *progress,
+ const gchar *help_domain,
+ const gchar *help_id)
+{
+ gimp_help_show (gimp, progress, help_domain, help_id);
+}
+
+static const gchar *
+gui_get_program_class (Gimp *gimp)
+{
+ return gdk_get_program_class ();
+}
+
+static gchar *
+gui_get_display_name (Gimp *gimp,
+ gint display_ID,
+ GObject **screen,
+ gint *monitor)
+{
+ GimpDisplay *display = NULL;
+ GdkScreen *my_screen = NULL;
+
+ if (display_ID > 0)
+ display = gimp_display_get_by_ID (gimp, display_ID);
+
+ if (display)
+ {
+ GimpDisplayShell *shell = gimp_display_get_shell (display);
+ GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (shell));
+
+ my_screen = gtk_widget_get_screen (GTK_WIDGET (shell));
+ *monitor = gdk_screen_get_monitor_at_window (my_screen, window);
+ }
+ else
+ {
+ *monitor = gui_get_initial_monitor (gimp, &my_screen);
+
+ if (*monitor == -1)
+ *monitor = gimp_get_monitor_at_pointer (&my_screen);
+ }
+
+ *screen = G_OBJECT (my_screen);
+
+ if (my_screen)
+ return gdk_screen_make_display_name (my_screen);
+
+ return NULL;
+}
+
+static guint32
+gui_get_user_time (Gimp *gimp)
+{
+#ifdef GDK_WINDOWING_X11
+ return gdk_x11_display_get_user_time (gdk_display_get_default ());
+#endif
+ return 0;
+}
+
+static GFile *
+gui_get_theme_dir (Gimp *gimp)
+{
+ return themes_get_theme_dir (gimp, GIMP_GUI_CONFIG (gimp->config)->theme);
+}
+
+static GFile *
+gui_get_icon_theme_dir (Gimp *gimp)
+{
+ return icon_themes_get_theme_dir (gimp, GIMP_GUI_CONFIG (gimp->config)->icon_theme);
+}
+
+static GimpObject *
+gui_get_window_strategy (Gimp *gimp)
+{
+ if (GIMP_GUI_CONFIG (gimp->config)->single_window_mode)
+ return gimp_single_window_strategy_get_singleton ();
+ else
+ return gimp_multi_window_strategy_get_singleton ();
+}
+
+static GimpObject *
+gui_get_empty_display (Gimp *gimp)
+{
+ GimpObject *display = NULL;
+
+ if (gimp_container_get_n_children (gimp->displays) == 1)
+ {
+ display = gimp_container_get_first_child (gimp->displays);
+
+ if (gimp_display_get_image (GIMP_DISPLAY (display)))
+ {
+ /* The display was not empty */
+ display = NULL;
+ }
+ }
+
+ return display;
+}
+
+static GimpObject *
+gui_display_get_by_ID (Gimp *gimp,
+ gint ID)
+{
+ return (GimpObject *) gimp_display_get_by_ID (gimp, ID);
+}
+
+static gint
+gui_display_get_ID (GimpObject *display)
+{
+ return gimp_display_get_ID (GIMP_DISPLAY (display));
+}
+
+static guint32
+gui_display_get_window_id (GimpObject *display)
+{
+ GimpDisplay *disp = GIMP_DISPLAY (display);
+ GimpDisplayShell *shell = gimp_display_get_shell (disp);
+
+ if (shell)
+ {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
+
+ if (GTK_IS_WINDOW (toplevel))
+ return gimp_window_get_native_id (GTK_WINDOW (toplevel));
+ }
+
+ return 0;
+}
+
+static GimpObject *
+gui_display_create (Gimp *gimp,
+ GimpImage *image,
+ GimpUnit unit,
+ gdouble scale,
+ GObject *screen,
+ gint monitor)
+{
+ GimpContext *context = gimp_get_user_context (gimp);
+ GimpDisplay *display = GIMP_DISPLAY (gui_get_empty_display (gimp));
+
+ if (! screen)
+ monitor = gimp_get_monitor_at_pointer ((GdkScreen **) &screen);
+
+ if (display)
+ {
+ gimp_display_fill (display, image, unit, scale);
+ }
+ else
+ {
+ GList *image_managers = gimp_ui_managers_from_name ("<Image>");
+
+ g_return_val_if_fail (image_managers != NULL, NULL);
+
+ display = gimp_display_new (gimp, image, unit, scale,
+ image_managers->data,
+ gimp_dialog_factory_get_singleton (),
+ GDK_SCREEN (screen),
+ monitor);
+ }
+
+ if (gimp_context_get_display (context) == display)
+ {
+ gimp_context_set_image (context, image);
+ gimp_context_display_changed (context);
+ }
+ else
+ {
+ gimp_context_set_display (context, display);
+ }
+
+ return GIMP_OBJECT (display);
+}
+
+static void
+gui_display_delete (GimpObject *display)
+{
+ gimp_display_close (GIMP_DISPLAY (display));
+}
+
+static void
+gui_displays_reconnect (Gimp *gimp,
+ GimpImage *old_image,
+ GimpImage *new_image)
+{
+ gimp_displays_reconnect (gimp, old_image, new_image);
+}
+
+static void
+gui_wait_input_async (GimpAsync *async,
+ const gint input_pipe[2])
+{
+ guint8 buffer[1];
+
+ while (read (input_pipe[0], buffer, sizeof (buffer)) == -1 &&
+ errno == EINTR);
+
+ gimp_async_finish (async, NULL);
+}
+
+static gboolean
+gui_wait (Gimp *gimp,
+ GimpWaitable *waitable,
+ const gchar *message)
+{
+ GimpProcedure *procedure;
+ GimpValueArray *args;
+ GimpAsync *input_async = NULL;
+ GError *error = NULL;
+ gint input_pipe[2];
+ gint output_pipe[2];
+
+ procedure = gimp_pdb_lookup_procedure (gimp->pdb, "plug-in-busy-dialog");
+
+ if (! procedure)
+ return FALSE;
+
+ if (pipe (input_pipe))
+ return FALSE;
+
+ if (pipe (output_pipe))
+ {
+ close (input_pipe[0]);
+ close (input_pipe[1]);
+
+ return FALSE;
+ }
+
+ gimp_spawn_set_cloexec (input_pipe[0]);
+ gimp_spawn_set_cloexec (output_pipe[1]);
+
+ args = gimp_procedure_get_arguments (procedure);
+ gimp_value_array_truncate (args, 5);
+
+ g_value_set_int (gimp_value_array_index (args, 0),
+ GIMP_RUN_INTERACTIVE);
+ g_value_set_int (gimp_value_array_index (args, 1),
+ output_pipe[0]);
+ g_value_set_int (gimp_value_array_index (args, 2),
+ input_pipe[1]);
+ g_value_set_string (gimp_value_array_index (args, 3),
+ message);
+ g_value_set_int (gimp_value_array_index (args, 4),
+ GIMP_IS_CANCELABLE (waitable));
+
+ gimp_procedure_execute_async (procedure, gimp,
+ gimp_get_user_context (gimp),
+ NULL, args, NULL, &error);
+
+ gimp_value_array_unref (args);
+
+ close (input_pipe[1]);
+ close (output_pipe[0]);
+
+ if (error)
+ {
+ g_clear_error (&error);
+
+ close (input_pipe[0]);
+ close (output_pipe[1]);
+
+ return FALSE;
+ }
+
+ if (GIMP_IS_CANCELABLE (waitable))
+ {
+ /* listens for a cancellation request */
+ input_async = gimp_parallel_run_async_independent (
+ (GimpRunAsyncFunc) gui_wait_input_async,
+ input_pipe);
+
+ while (! gimp_waitable_wait_for (waitable, 0.1 * G_TIME_SPAN_SECOND))
+ {
+ /* check for a cancellation request */
+ if (gimp_waitable_try_wait (GIMP_WAITABLE (input_async)))
+ {
+ gimp_cancelable_cancel (GIMP_CANCELABLE (waitable));
+
+ break;
+ }
+ }
+ }
+
+ gimp_waitable_wait (waitable);
+
+ /* signal completion to the plug-in */
+ close (output_pipe[1]);
+
+ if (input_async)
+ {
+ gimp_waitable_wait (GIMP_WAITABLE (input_async));
+
+ g_object_unref (input_async);
+ }
+
+ close (input_pipe[0]);
+
+ return TRUE;
+}
+
+static GimpProgress *
+gui_new_progress (Gimp *gimp,
+ GimpObject *display)
+{
+ g_return_val_if_fail (display == NULL || GIMP_IS_DISPLAY (display), NULL);
+
+ if (display)
+ return GIMP_PROGRESS (display);
+
+ return GIMP_PROGRESS (gimp_progress_dialog_new ());
+}
+
+static void
+gui_free_progress (Gimp *gimp,
+ GimpProgress *progress)
+{
+ g_return_if_fail (GIMP_IS_PROGRESS_DIALOG (progress));
+
+ if (GIMP_IS_PROGRESS_DIALOG (progress))
+ gtk_widget_destroy (GTK_WIDGET (progress));
+}
+
+static gboolean
+gui_pdb_dialog_present (GtkWindow *window)
+{
+ gtk_window_present (window);
+
+ return FALSE;
+}
+
+static gboolean
+gui_pdb_dialog_new (Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpContainer *container,
+ const gchar *title,
+ const gchar *callback_name,
+ const gchar *object_name,
+ va_list args)
+{
+ GType dialog_type = G_TYPE_NONE;
+ const gchar *dialog_role = NULL;
+ const gchar *help_id = NULL;
+
+ if (gimp_container_get_children_type (container) == GIMP_TYPE_BRUSH)
+ {
+ dialog_type = GIMP_TYPE_BRUSH_SELECT;
+ dialog_role = "gimp-brush-selection";
+ help_id = GIMP_HELP_BRUSH_DIALOG;
+ }
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_FONT)
+ {
+ dialog_type = GIMP_TYPE_FONT_SELECT;
+ dialog_role = "gimp-font-selection";
+ help_id = GIMP_HELP_FONT_DIALOG;
+ }
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_GRADIENT)
+ {
+ dialog_type = GIMP_TYPE_GRADIENT_SELECT;
+ dialog_role = "gimp-gradient-selection";
+ help_id = GIMP_HELP_GRADIENT_DIALOG;
+ }
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_PALETTE)
+ {
+ dialog_type = GIMP_TYPE_PALETTE_SELECT;
+ dialog_role = "gimp-palette-selection";
+ help_id = GIMP_HELP_PALETTE_DIALOG;
+ }
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_PATTERN)
+ {
+ dialog_type = GIMP_TYPE_PATTERN_SELECT;
+ dialog_role = "gimp-pattern-selection";
+ help_id = GIMP_HELP_PATTERN_DIALOG;
+ }
+
+ if (dialog_type != G_TYPE_NONE)
+ {
+ GimpObject *object = NULL;
+
+ if (object_name && strlen (object_name))
+ object = gimp_container_get_child_by_name (container, object_name);
+
+ if (! object)
+ object = gimp_context_get_by_type (context,
+ gimp_container_get_children_type (container));
+
+ if (object)
+ {
+ gint n_properties = 0;
+ gchar **names = NULL;
+ GValue *values = NULL;
+ GtkWidget *dialog;
+ GtkWidget *view;
+
+ names = gimp_properties_append (dialog_type,
+ &n_properties, names, &values,
+ "title", title,
+ "role", dialog_role,
+ "help-func", gimp_standard_help_func,
+ "help-id", help_id,
+ "pdb", gimp->pdb,
+ "context", context,
+ "select-type", gimp_container_get_children_type (container),
+ "initial-object", object,
+ "callback-name", callback_name,
+ "menu-factory", global_menu_factory,
+ NULL);
+
+ names = gimp_properties_append_valist (dialog_type,
+ &n_properties, names, &values,
+ args);
+
+ dialog = (GtkWidget *)
+ g_object_new_with_properties (dialog_type,
+ n_properties,
+ (const gchar **) names,
+ (const GValue *) values);
+
+ gimp_properties_free (n_properties, names, values);
+
+ view = GIMP_PDB_DIALOG (dialog)->view;
+ if (view)
+ gimp_docked_set_show_button_bar (GIMP_DOCKED (view), FALSE);
+
+ if (progress)
+ {
+ guint32 window_id = gimp_progress_get_window_id (progress);
+
+ if (window_id)
+ gimp_window_set_transient_for (GTK_WINDOW (dialog), window_id);
+ }
+
+ gtk_widget_show (dialog);
+
+ /* workaround for bug #360106 */
+ {
+ GSource *source = g_timeout_source_new (100);
+ GClosure *closure;
+
+ closure = g_cclosure_new_object (G_CALLBACK (gui_pdb_dialog_present),
+ G_OBJECT (dialog));
+
+ g_source_set_closure (source, closure);
+ g_source_attach (source, NULL);
+ g_source_unref (source);
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gui_pdb_dialog_set (Gimp *gimp,
+ GimpContainer *container,
+ const gchar *callback_name,
+ const gchar *object_name,
+ va_list args)
+{
+ GimpPdbDialogClass *klass = NULL;
+
+ if (gimp_container_get_children_type (container) == GIMP_TYPE_BRUSH)
+ klass = g_type_class_peek (GIMP_TYPE_BRUSH_SELECT);
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_FONT)
+ klass = g_type_class_peek (GIMP_TYPE_FONT_SELECT);
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_GRADIENT)
+ klass = g_type_class_peek (GIMP_TYPE_GRADIENT_SELECT);
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_PALETTE)
+ klass = g_type_class_peek (GIMP_TYPE_PALETTE_SELECT);
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_PATTERN)
+ klass = g_type_class_peek (GIMP_TYPE_PATTERN_SELECT);
+
+ if (klass)
+ {
+ GimpPdbDialog *dialog;
+
+ dialog = gimp_pdb_dialog_get_by_callback (klass, callback_name);
+
+ if (dialog && dialog->select_type == gimp_container_get_children_type (container))
+ {
+ GimpObject *object;
+
+ object = gimp_container_get_child_by_name (container, object_name);
+
+ if (object)
+ {
+ const gchar *prop_name = va_arg (args, const gchar *);
+
+ gimp_context_set_by_type (dialog->context, dialog->select_type,
+ object);
+
+ if (prop_name)
+ g_object_set_valist (G_OBJECT (dialog), prop_name, args);
+
+ gtk_window_present (GTK_WINDOW (dialog));
+
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gui_pdb_dialog_close (Gimp *gimp,
+ GimpContainer *container,
+ const gchar *callback_name)
+{
+ GimpPdbDialogClass *klass = NULL;
+
+ if (gimp_container_get_children_type (container) == GIMP_TYPE_BRUSH)
+ klass = g_type_class_peek (GIMP_TYPE_BRUSH_SELECT);
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_FONT)
+ klass = g_type_class_peek (GIMP_TYPE_FONT_SELECT);
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_GRADIENT)
+ klass = g_type_class_peek (GIMP_TYPE_GRADIENT_SELECT);
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_PALETTE)
+ klass = g_type_class_peek (GIMP_TYPE_PALETTE_SELECT);
+ else if (gimp_container_get_children_type (container) == GIMP_TYPE_PATTERN)
+ klass = g_type_class_peek (GIMP_TYPE_PATTERN_SELECT);
+
+ if (klass)
+ {
+ GimpPdbDialog *dialog;
+
+ dialog = gimp_pdb_dialog_get_by_callback (klass, callback_name);
+
+ if (dialog && dialog->select_type == gimp_container_get_children_type (container))
+ {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gui_recent_list_add_file (Gimp *gimp,
+ GFile *file,
+ const gchar *mime_type)
+{
+ GtkRecentData recent;
+ const gchar *groups[2] = { "Graphics", NULL };
+ gchar *uri;
+ gboolean success;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ /* use last part of the URI */
+ recent.display_name = NULL;
+
+ /* no special description */
+ recent.description = NULL;
+ recent.mime_type = (mime_type ?
+ (gchar *) mime_type : "application/octet-stream");
+ recent.app_name = "GNU Image Manipulation Program";
+ recent.app_exec = GIMP_COMMAND " %u";
+ recent.groups = (gchar **) groups;
+ recent.is_private = FALSE;
+
+ uri = g_file_get_uri (file);
+
+ success = gtk_recent_manager_add_full (gtk_recent_manager_get_default (),
+ uri, &recent);
+
+ g_free (uri);
+
+ return success;
+}
+
+static gint
+gui_recent_list_compare (gconstpointer a,
+ gconstpointer b)
+{
+ return (gtk_recent_info_get_modified ((GtkRecentInfo *) a) -
+ gtk_recent_info_get_modified ((GtkRecentInfo *) b));
+}
+
+static void
+gui_recent_list_load (Gimp *gimp)
+{
+ GList *items;
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ gimp_container_freeze (gimp->documents);
+ gimp_container_clear (gimp->documents);
+
+ items = gtk_recent_manager_get_items (gtk_recent_manager_get_default ());
+
+ items = g_list_sort (items, gui_recent_list_compare);
+
+ for (list = items; list; list = list->next)
+ {
+ GtkRecentInfo *info = list->data;
+
+ if (gtk_recent_info_has_application (info,
+ "GNU Image Manipulation Program"))
+ {
+ const gchar *mime_type = gtk_recent_info_get_mime_type (info);
+
+ if (mime_type &&
+ gimp_plug_in_manager_file_procedure_find_by_mime_type (gimp->plug_in_manager,
+ GIMP_FILE_PROCEDURE_GROUP_OPEN,
+ mime_type))
+ {
+ GimpImagefile *imagefile;
+ GFile *file;
+
+ file = g_file_new_for_uri (gtk_recent_info_get_uri (info));
+ imagefile = gimp_imagefile_new (gimp, file);
+ g_object_unref (file);
+
+ gimp_imagefile_set_mime_type (imagefile, mime_type);
+
+ gimp_container_add (gimp->documents, GIMP_OBJECT (imagefile));
+ g_object_unref (imagefile);
+ }
+ }
+
+ gtk_recent_info_unref (info);
+ }
+
+ g_list_free (items);
+
+ gimp_container_thaw (gimp->documents);
+}
+
+static GMountOperation *
+gui_get_mount_operation (Gimp *gimp,
+ GimpProgress *progress)
+{
+ GtkWidget *toplevel = NULL;
+
+ if (GTK_IS_WIDGET (progress))
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (progress));
+
+ return gtk_mount_operation_new (GTK_WINDOW (toplevel));
+}
+
+static GimpColorProfilePolicy
+gui_query_profile_policy (Gimp *gimp,
+ GimpImage *image,
+ GimpContext *context,
+ GimpColorProfile **dest_profile,
+ GimpColorRenderingIntent *intent,
+ gboolean *bpc,
+ gboolean *dont_ask)
+{
+ return color_profile_import_dialog_run (image, context, NULL,
+ dest_profile,
+ intent, bpc,
+ dont_ask);
+}
diff --git a/app/gui/gui-vtable.h b/app/gui/gui-vtable.h
new file mode 100644
index 0000000..e52009b
--- /dev/null
+++ b/app/gui/gui-vtable.h
@@ -0,0 +1,31 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GUI_VTABLE_H__
+#define __GUI_VTABLE_H__
+
+
+void gui_vtable_init (Gimp *gimp);
+
+/* this function lives in gui.c but must only be used from gui-vtable.c;
+ * also, gui.h can't contain any Gdk types.
+ */
+gint gui_get_initial_monitor (Gimp *gimp,
+ GdkScreen **screen);
+
+
+#endif /* __GUI_VTABLE_H__ */
diff --git a/app/gui/gui.c b/app/gui/gui.c
new file mode 100644
index 0000000..e5928eb
--- /dev/null
+++ b/app/gui/gui.c
@@ -0,0 +1,1077 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+#include "libgimpwidgets/gimpwidgets-private.h"
+
+#include "gui-types.h"
+
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimpcontainer.h"
+#include "core/gimpcontext.h"
+#include "core/gimpimage.h"
+#include "core/gimptoolinfo.h"
+
+#include "plug-in/gimpenvirontable.h"
+#include "plug-in/gimppluginmanager.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimpdisplay-foreach.h"
+#include "display/gimpdisplayshell.h"
+#include "display/gimpstatusbar.h"
+
+#include "tools/gimp-tools.h"
+#include "tools/gimptool.h"
+#include "tools/tool_manager.h"
+
+#include "widgets/gimpaction.h"
+#include "widgets/gimpactiongroup.h"
+#include "widgets/gimpaction-history.h"
+#include "widgets/gimpclipboard.h"
+#include "widgets/gimpcolorselectorpalette.h"
+#include "widgets/gimpcontrollers.h"
+#include "widgets/gimpdevices.h"
+#include "widgets/gimpdialogfactory.h"
+#include "widgets/gimpdnd.h"
+#include "widgets/gimprender.h"
+#include "widgets/gimphelp.h"
+#include "widgets/gimphelp-ids.h"
+#include "widgets/gimpmenufactory.h"
+#include "widgets/gimpmessagebox.h"
+#include "widgets/gimpsessioninfo.h"
+#include "widgets/gimpuimanager.h"
+#include "widgets/gimpwidgets-utils.h"
+#include "widgets/gimplanguagestore-parser.h"
+
+#include "actions/actions.h"
+#include "actions/windows-commands.h"
+
+#include "menus/menus.h"
+
+#include "dialogs/dialogs.h"
+
+#include "gimpuiconfigurer.h"
+#include "gui.h"
+#include "gui-unique.h"
+#include "gui-vtable.h"
+#include "icon-themes.h"
+#include "session.h"
+#include "splash.h"
+#include "themes.h"
+
+#ifdef GDK_WINDOWING_QUARTZ
+#import <AppKit/AppKit.h>
+#include <gtkosxapplication.h>
+#include <gdk/gdkquartz.h>
+
+/* Forward declare since we are building against old SDKs. */
+#if !defined(MAC_OS_X_VERSION_10_12) || \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12
+
+@interface NSWindow(ForwardDeclarations)
++ (void)setAllowsAutomaticWindowTabbing:(BOOL)allow;
+@end
+
+#endif
+
+#endif /* GDK_WINDOWING_QUARTZ */
+
+#include "gimp-intl.h"
+
+
+/* local function prototypes */
+
+static gchar * gui_sanity_check (void);
+static void gui_help_func (const gchar *help_id,
+ gpointer help_data);
+static gboolean gui_get_background_func (GimpRGB *color);
+static gboolean gui_get_foreground_func (GimpRGB *color);
+
+static void gui_initialize_after_callback (Gimp *gimp,
+ GimpInitStatusFunc callback);
+
+static void gui_restore_callback (Gimp *gimp,
+ GimpInitStatusFunc callback);
+static void gui_restore_after_callback (Gimp *gimp,
+ GimpInitStatusFunc callback);
+
+static gboolean gui_exit_callback (Gimp *gimp,
+ gboolean force);
+static gboolean gui_exit_after_callback (Gimp *gimp,
+ gboolean force);
+
+static void gui_show_tooltips_notify (GimpGuiConfig *gui_config,
+ GParamSpec *pspec,
+ Gimp *gimp);
+static void gui_show_help_button_notify (GimpGuiConfig *gui_config,
+ GParamSpec *pspec,
+ Gimp *gimp);
+static void gui_user_manual_notify (GimpGuiConfig *gui_config,
+ GParamSpec *pspec,
+ Gimp *gimp);
+static void gui_single_window_mode_notify (GimpGuiConfig *gui_config,
+ GParamSpec *pspec,
+ GimpUIConfigurer *ui_configurer);
+static void gui_tearoff_menus_notify (GimpGuiConfig *gui_config,
+ GParamSpec *pspec,
+ GtkUIManager *manager);
+
+static void gui_clipboard_changed (Gimp *gimp);
+
+static void gui_menu_show_tooltip (GimpUIManager *manager,
+ const gchar *tooltip,
+ Gimp *gimp);
+static void gui_menu_hide_tooltip (GimpUIManager *manager,
+ Gimp *gimp);
+
+static void gui_display_changed (GimpContext *context,
+ GimpDisplay *display,
+ Gimp *gimp);
+
+static void gui_compare_accelerator (gpointer data,
+ const gchar *accel_path,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ gboolean changed);
+static void gui_check_unique_accelerator (gpointer data,
+ const gchar *accel_path,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ gboolean changed);
+static gboolean gui_check_action_exists (const gchar *accel_path);
+
+
+/* private variables */
+
+static Gimp *the_gui_gimp = NULL;
+static GimpUIManager *image_ui_manager = NULL;
+static GimpUIConfigurer *ui_configurer = NULL;
+static GdkScreen *initial_screen = NULL;
+static gint initial_monitor = -1;
+
+
+/* public functions */
+
+void
+gui_libs_init (GOptionContext *context)
+{
+ g_return_if_fail (context != NULL);
+
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+}
+
+void
+gui_abort (const gchar *abort_message)
+{
+ GtkWidget *dialog;
+ GtkWidget *box;
+
+ g_return_if_fail (abort_message != NULL);
+
+ dialog = gimp_dialog_new (_("GIMP Message"), "gimp-abort",
+ NULL, GTK_DIALOG_MODAL, NULL, NULL,
+
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ box = g_object_new (GIMP_TYPE_MESSAGE_BOX,
+ "icon-name", GIMP_ICON_WILBER_EEK,
+ "border-width", 12,
+ NULL);
+
+ gimp_message_box_set_text (GIMP_MESSAGE_BOX (box), "%s", abort_message);
+
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ box, TRUE, TRUE, 0);
+ gtk_widget_show (box);
+
+ gimp_dialog_run (GIMP_DIALOG (dialog));
+
+ exit (EXIT_FAILURE);
+}
+
+GimpInitStatusFunc
+gui_init (Gimp *gimp,
+ gboolean no_splash)
+{
+ GimpInitStatusFunc status_callback = NULL;
+ gchar *abort_message;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+ g_return_val_if_fail (the_gui_gimp == NULL, NULL);
+
+ abort_message = gui_sanity_check ();
+ if (abort_message)
+ gui_abort (abort_message);
+
+ the_gui_gimp = gimp;
+
+ /* TRANSLATORS: there is no need to translate this in GIMP. This uses
+ * "gtk20" domain as a special trick to determine language direction,
+ * but xgettext extracts it anyway mistakenly into GIMP po files.
+ * Leave an empty string as translation. It does not matter.
+ */
+ if (g_strcmp0 (dgettext ("gtk20", "default:LTR"), "default:RTL") == 0)
+ /* Normally this should have been taken care of during command line
+ * parsing as a post-parse hook of gtk_get_option_group(), using the
+ * system locales.
+ * But user config may have overridden the language, therefore we must
+ * check the widget directions again.
+ */
+ gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
+ else
+ gtk_widget_set_default_direction (GTK_TEXT_DIR_LTR);
+
+ gui_unique_init (gimp);
+ gimp_language_store_parser_init ();
+
+ /* initialize icon themes before gimp_widgets_init() so we avoid
+ * setting the configured theme twice
+ */
+ icon_themes_init (gimp);
+
+ gimp_widgets_init (gui_help_func,
+ gui_get_foreground_func,
+ gui_get_background_func,
+ NULL);
+
+ g_type_class_ref (GIMP_TYPE_COLOR_SELECT);
+
+ /* disable automatic startup notification */
+ gtk_window_set_auto_startup_notification (FALSE);
+
+#ifdef GDK_WINDOWING_QUARTZ
+ /* Before the first window is created (typically the splash window),
+ * we need to disable automatic tabbing behavior introduced on Sierra.
+ * This is known to cause all kinds of weird issues (see for instance
+ * Bugzilla #776294) and needs proper GTK+ support if we would want to
+ * enable it.
+ */
+ if ([NSWindow respondsToSelector:@selector(setAllowsAutomaticWindowTabbing:)])
+ [NSWindow setAllowsAutomaticWindowTabbing:NO];
+
+ /* MacOS 11 (Big Sur) has added a new, dynamic "accent" as default.
+ * This uses a 10-bit colorspace so every GIMP drawing operation
+ * has the additional cost of an 8-bit (ARGB) to 10-bit conversion.
+ * Let's disable this mode to regain the lost performance.
+ */
+ if (gdk_quartz_osx_version () >= GDK_OSX_BIG_SUR)
+ {
+ NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+ [userDefaults setBool: NO forKey:@"NSViewUsesAutomaticLayerBackingStores"];
+ }
+
+#endif /* GDK_WINDOWING_QUARTZ */
+
+ gimp_dnd_init (gimp);
+
+ themes_init (gimp);
+
+ initial_monitor = gimp_get_monitor_at_pointer (&initial_screen);
+ gtk_widget_set_default_colormap (gdk_screen_get_rgb_colormap (initial_screen));
+
+ if (! no_splash)
+ {
+ splash_create (gimp->be_verbose, initial_screen, initial_monitor);
+ status_callback = splash_update;
+ }
+
+ g_signal_connect_after (gimp, "initialize",
+ G_CALLBACK (gui_initialize_after_callback),
+ NULL);
+
+ g_signal_connect (gimp, "restore",
+ G_CALLBACK (gui_restore_callback),
+ NULL);
+ g_signal_connect_after (gimp, "restore",
+ G_CALLBACK (gui_restore_after_callback),
+ NULL);
+
+ g_signal_connect (gimp, "exit",
+ G_CALLBACK (gui_exit_callback),
+ NULL);
+ g_signal_connect_after (gimp, "exit",
+ G_CALLBACK (gui_exit_after_callback),
+ NULL);
+
+ return status_callback;
+}
+
+/*
+ * gui_recover:
+ * @n_recoveries: number of recovered files.
+ *
+ * Query the user interactively if files were saved from a previous
+ * crash, asking whether to try and recover or discard them.
+ *
+ * Returns: TRUE if answer is to try and recover, FALSE otherwise.
+ */
+gboolean
+gui_recover (gint n_recoveries)
+{
+ GtkWidget *dialog;
+ GtkWidget *box;
+ gboolean recover;
+
+ dialog = gimp_dialog_new (_("Image Recovery"), "gimp-recovery",
+ NULL, GTK_DIALOG_MODAL, NULL, NULL,
+ _("_Discard"), GTK_RESPONSE_CANCEL,
+ _("_Recover"), GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK);
+
+ box = gimp_message_box_new (GIMP_ICON_WILBER_EEK);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ box, TRUE, TRUE, 0);
+ gtk_widget_show (box);
+
+ gimp_message_box_set_primary_text (GIMP_MESSAGE_BOX (box),
+ _("Eeek! It looks like GIMP recovered from a crash!"));
+
+ gimp_message_box_set_text (GIMP_MESSAGE_BOX (box),
+ /* TRANSLATORS: even if English singular form does
+ * not use %d, you can use %d for translation in
+ * any singular/plural form of your language if
+ * suited. It will just work and be replaced by the
+ * number of images as expected.
+ */
+ ngettext ("An image was salvaged from the crash. "
+ "Do you want to try and recover it?",
+ "%d images were salvaged from the crash. "
+ "Do you want to try and recover them?",
+ n_recoveries), n_recoveries);
+
+ recover = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+ gtk_widget_destroy (dialog);
+
+ return recover;
+}
+
+gint
+gui_get_initial_monitor (Gimp *gimp,
+ GdkScreen **screen)
+{
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), 0);
+ g_return_val_if_fail (screen != NULL, 0);
+
+ *screen = initial_screen;
+
+ return initial_monitor;
+}
+
+
+/* private functions */
+
+static gchar *
+gui_sanity_check (void)
+{
+#define GTK_REQUIRED_MAJOR 2
+#define GTK_REQUIRED_MINOR 24
+#define GTK_REQUIRED_MICRO 10
+
+ const gchar *mismatch = gtk_check_version (GTK_REQUIRED_MAJOR,
+ GTK_REQUIRED_MINOR,
+ GTK_REQUIRED_MICRO);
+
+ if (mismatch)
+ {
+ return g_strdup_printf
+ ("%s\n\n"
+ "GIMP requires GTK+ version %d.%d.%d or later.\n"
+ "Installed GTK+ version is %d.%d.%d.\n\n"
+ "Somehow you or your software packager managed\n"
+ "to install GIMP with an older GTK+ version.\n\n"
+ "Please upgrade to GTK+ version %d.%d.%d or later.",
+ mismatch,
+ GTK_REQUIRED_MAJOR, GTK_REQUIRED_MINOR, GTK_REQUIRED_MICRO,
+ gtk_major_version, gtk_minor_version, gtk_micro_version,
+ GTK_REQUIRED_MAJOR, GTK_REQUIRED_MINOR, GTK_REQUIRED_MICRO);
+ }
+
+#undef GTK_REQUIRED_MAJOR
+#undef GTK_REQUIRED_MINOR
+#undef GTK_REQUIRED_MICRO
+
+ return NULL;
+}
+
+static void
+gui_help_func (const gchar *help_id,
+ gpointer help_data)
+{
+ g_return_if_fail (GIMP_IS_GIMP (the_gui_gimp));
+
+ gimp_help (the_gui_gimp, NULL, NULL, help_id);
+}
+
+static gboolean
+gui_get_foreground_func (GimpRGB *color)
+{
+ g_return_val_if_fail (color != NULL, FALSE);
+ g_return_val_if_fail (GIMP_IS_GIMP (the_gui_gimp), FALSE);
+
+ gimp_context_get_foreground (gimp_get_user_context (the_gui_gimp), color);
+
+ return TRUE;
+}
+
+static gboolean
+gui_get_background_func (GimpRGB *color)
+{
+ g_return_val_if_fail (color != NULL, FALSE);
+ g_return_val_if_fail (GIMP_IS_GIMP (the_gui_gimp), FALSE);
+
+ gimp_context_get_background (gimp_get_user_context (the_gui_gimp), color);
+
+ return TRUE;
+}
+
+static void
+gui_initialize_after_callback (Gimp *gimp,
+ GimpInitStatusFunc status_callback)
+{
+ const gchar *name = NULL;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ if (gimp->be_verbose)
+ g_print ("INIT: %s\n", G_STRFUNC);
+
+#if defined (GDK_WINDOWING_X11)
+ name = "DISPLAY";
+#elif defined (GDK_WINDOWING_DIRECTFB) || defined (GDK_WINDOWING_FB)
+ name = "GDK_DISPLAY";
+#endif
+
+ /* TODO: Need to care about display migration with GTK+ 2.2 at some point */
+
+ if (name)
+ {
+ gchar *display = gdk_get_display ();
+
+ gimp_environ_table_add (gimp->plug_in_manager->environ_table,
+ name, display, NULL);
+ g_free (display);
+ }
+
+ gimp_tools_init (gimp);
+
+ gimp_context_set_tool (gimp_get_user_context (gimp),
+ gimp_tool_info_get_standard (gimp));
+}
+
+static void
+gui_restore_callback (Gimp *gimp,
+ GimpInitStatusFunc status_callback)
+{
+ GimpDisplayConfig *display_config = GIMP_DISPLAY_CONFIG (gimp->config);
+ GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config);
+
+ if (gimp->be_verbose)
+ g_print ("INIT: %s\n", G_STRFUNC);
+
+ gui_vtable_init (gimp);
+
+ if (! gui_config->show_tooltips)
+ gimp_help_disable_tooltips ();
+
+ g_signal_connect (gui_config, "notify::show-tooltips",
+ G_CALLBACK (gui_show_tooltips_notify),
+ gimp);
+
+ gimp_dialogs_show_help_button (gui_config->use_help &&
+ gui_config->show_help_button);
+
+ g_signal_connect (gui_config, "notify::use-help",
+ G_CALLBACK (gui_show_help_button_notify),
+ gimp);
+ g_signal_connect (gui_config, "notify::user-manual-online",
+ G_CALLBACK (gui_user_manual_notify),
+ gimp);
+ g_signal_connect (gui_config, "notify::show-help-button",
+ G_CALLBACK (gui_show_help_button_notify),
+ gimp);
+
+ g_signal_connect (gimp_get_user_context (gimp), "display-changed",
+ G_CALLBACK (gui_display_changed),
+ gimp);
+
+ /* make sure the monitor resolution is valid */
+ if (display_config->monitor_res_from_gdk ||
+ display_config->monitor_xres < GIMP_MIN_RESOLUTION ||
+ display_config->monitor_yres < GIMP_MIN_RESOLUTION)
+ {
+ gdouble xres, yres;
+
+ gimp_get_monitor_resolution (initial_screen,
+ initial_monitor,
+ &xres, &yres);
+
+ g_object_set (gimp->config,
+ "monitor-xresolution", xres,
+ "monitor-yresolution", yres,
+ "monitor-resolution-from-windowing-system", TRUE,
+ NULL);
+ }
+
+ actions_init (gimp);
+ menus_init (gimp, global_action_factory);
+ gimp_render_init (gimp);
+
+ dialogs_init (gimp, global_menu_factory);
+
+ gimp_clipboard_init (gimp);
+ if (gimp_get_clipboard_image (gimp))
+ gimp_clipboard_set_image (gimp, gimp_get_clipboard_image (gimp));
+ else
+ gimp_clipboard_set_buffer (gimp, gimp_get_clipboard_buffer (gimp));
+
+ g_signal_connect (gimp, "clipboard-changed",
+ G_CALLBACK (gui_clipboard_changed),
+ NULL);
+
+ gimp_devices_init (gimp);
+ gimp_controllers_init (gimp);
+ session_init (gimp);
+
+ g_type_class_unref (g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_PALETTE));
+
+ status_callback (NULL, _("Tool Options"), 1.0);
+ gimp_tools_restore (gimp);
+}
+
+#ifdef GDK_WINDOWING_QUARTZ
+static void
+gui_add_to_app_menu (GimpUIManager *ui_manager,
+ GtkosxApplication *osx_app,
+ const gchar *action_path,
+ gint index)
+{
+ GtkWidget *item;
+
+ item = gimp_ui_manager_get_widget (ui_manager, action_path);
+
+ if (GTK_IS_MENU_ITEM (item))
+ gtkosx_application_insert_app_menu_item (osx_app, GTK_WIDGET (item), index);
+}
+
+static gboolean
+gui_quartz_quit_callback (GtkosxApplication *osx_app,
+ GimpUIManager *ui_manager)
+{
+ gimp_ui_manager_activate_action (ui_manager, "file", "file-quit");
+
+ return TRUE;
+}
+#endif
+
+static void
+gui_restore_after_callback (Gimp *gimp,
+ GimpInitStatusFunc status_callback)
+{
+ GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config);
+ GimpDisplay *display;
+
+ if (gimp->be_verbose)
+ g_print ("INIT: %s\n", G_STRFUNC);
+
+ gimp->message_handler = GIMP_MESSAGE_BOX;
+
+ /* load the recent documents after gimp_real_restore() because we
+ * need the mime-types implemented by plug-ins
+ */
+ status_callback (NULL, _("Documents"), 0.9);
+ gimp_recent_list_load (gimp);
+
+ /* enable this to always have icons everywhere */
+ if (g_getenv ("GIMP_ICONS_LIKE_A_BOSS"))
+ {
+ GdkScreen *screen = gdk_screen_get_default ();
+
+ g_object_set (G_OBJECT (gtk_settings_get_for_screen (screen)),
+ "gtk-button-images", TRUE,
+ "gtk-menu-images", TRUE,
+ NULL);
+ }
+
+ if (gui_config->restore_accels)
+ menus_restore (gimp);
+
+ ui_configurer = g_object_new (GIMP_TYPE_UI_CONFIGURER,
+ "gimp", gimp,
+ NULL);
+
+ image_ui_manager = gimp_menu_factory_manager_new (global_menu_factory,
+ "<Image>",
+ gimp,
+ gui_config->tearoff_menus);
+ gimp_ui_manager_update (image_ui_manager, gimp);
+
+ /* Check that every accelerator is unique. */
+ gtk_accel_map_foreach_unfiltered (NULL,
+ gui_check_unique_accelerator);
+
+ gimp_action_history_init (gimp);
+
+ g_signal_connect_object (gui_config, "notify::single-window-mode",
+ G_CALLBACK (gui_single_window_mode_notify),
+ ui_configurer, 0);
+ g_signal_connect_object (gui_config, "notify::tearoff-menus",
+ G_CALLBACK (gui_tearoff_menus_notify),
+ image_ui_manager, 0);
+ g_signal_connect (image_ui_manager, "show-tooltip",
+ G_CALLBACK (gui_menu_show_tooltip),
+ gimp);
+ g_signal_connect (image_ui_manager, "hide-tooltip",
+ G_CALLBACK (gui_menu_hide_tooltip),
+ gimp);
+
+ gimp_devices_restore (gimp);
+ gimp_controllers_restore (gimp, image_ui_manager);
+
+ if (status_callback == splash_update)
+ splash_destroy ();
+
+ if (gimp_get_show_gui (gimp))
+ {
+ GimpDisplayShell *shell;
+ GtkWidget *toplevel;
+
+ /* create the empty display */
+ display = GIMP_DISPLAY (gimp_create_display (gimp, NULL,
+ GIMP_UNIT_PIXEL, 1.0,
+ G_OBJECT (initial_screen),
+ initial_monitor));
+
+ shell = gimp_display_get_shell (display);
+
+ if (gui_config->restore_session)
+ session_restore (gimp,
+ initial_screen,
+ initial_monitor);
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
+
+#ifdef GDK_WINDOWING_QUARTZ
+ {
+ GtkosxApplication *osx_app;
+ GtkWidget *menu;
+ GtkWidget *item;
+
+ [[NSUserDefaults standardUserDefaults] setObject:@"NO"
+ forKey:@"NSTreatUnknownArgumentsAsOpen"];
+
+ osx_app = gtkosx_application_get ();
+
+ menu = gimp_ui_manager_get_widget (image_ui_manager,
+ "/image-menubar");
+ /* menu should have window parent for accelerator support */
+ gtk_widget_set_parent(menu, toplevel);
+
+ if (GTK_IS_MENU_ITEM (menu))
+ menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
+
+ /* do not activate OSX menu if tests are running */
+ if (! g_getenv ("GIMP_TESTING_ABS_TOP_SRCDIR"))
+ gtkosx_application_set_menu_bar (osx_app, GTK_MENU_SHELL (menu));
+
+ gtkosx_application_set_use_quartz_accelerators (osx_app, FALSE);
+
+ gui_add_to_app_menu (image_ui_manager, osx_app,
+ "/image-menubar/Help/dialogs-about", 0);
+ gui_add_to_app_menu (image_ui_manager, osx_app,
+ "/image-menubar/Help/dialogs-search-action", 1);
+
+#define PREFERENCES "/image-menubar/Edit/Preferences/"
+
+ gui_add_to_app_menu (image_ui_manager, osx_app,
+ PREFERENCES "dialogs-preferences", 3);
+ gui_add_to_app_menu (image_ui_manager, osx_app,
+ PREFERENCES "dialogs-input-devices", 4);
+ gui_add_to_app_menu (image_ui_manager, osx_app,
+ PREFERENCES "dialogs-keyboard-shortcuts", 5);
+ gui_add_to_app_menu (image_ui_manager, osx_app,
+ PREFERENCES "dialogs-module-dialog", 6);
+ gui_add_to_app_menu (image_ui_manager, osx_app,
+ PREFERENCES "plug-in-unit-editor", 7);
+
+#undef PREFERENCES
+
+ item = gtk_separator_menu_item_new ();
+ gtkosx_application_insert_app_menu_item (osx_app, item, 8);
+
+ item = gimp_ui_manager_get_widget (image_ui_manager,
+ "/image-menubar/File/file-quit");
+ gtk_widget_hide (item);
+
+ g_signal_connect (osx_app, "NSApplicationBlockTermination",
+ G_CALLBACK (gui_quartz_quit_callback),
+ image_ui_manager);
+
+ gtkosx_application_ready (osx_app);
+ }
+#endif /* GDK_WINDOWING_QUARTZ */
+
+ /* move keyboard focus to the display */
+ gtk_window_present (GTK_WINDOW (toplevel));
+ }
+
+ /* indicate that the application has finished loading */
+ gdk_notify_startup_complete ();
+
+ /* clear startup monitor variables */
+ initial_screen = NULL;
+ initial_monitor = -1;
+}
+
+static gboolean
+gui_exit_callback (Gimp *gimp,
+ gboolean force)
+{
+ GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (gimp->config);
+ GimpTool *active_tool;
+
+ if (gimp->be_verbose)
+ g_print ("EXIT: %s\n", G_STRFUNC);
+
+ if (! force && gimp_displays_dirty (gimp))
+ {
+ GdkScreen *screen;
+ gint monitor;
+
+ monitor = gimp_get_monitor_at_pointer (&screen);
+
+ gimp_dialog_factory_dialog_raise (gimp_dialog_factory_get_singleton (),
+ screen, monitor,
+ "gimp-quit-dialog", -1);
+
+ return TRUE; /* stop exit for now */
+ }
+
+ gimp->message_handler = GIMP_CONSOLE;
+
+ gui_unique_exit ();
+
+ /* If any modifier is set when quitting (typically when exiting with
+ * Ctrl-q for instance!), when serializing the tool options, it will
+ * save any alternate value instead of the main one. Make sure that
+ * any modifier is reset before saving options.
+ */
+ active_tool = tool_manager_get_active (gimp);
+ if (active_tool && active_tool->focus_display)
+ gimp_tool_set_modifier_state (active_tool, 0, active_tool->focus_display);
+
+ if (gui_config->save_session_info)
+ session_save (gimp, FALSE);
+
+ if (gui_config->save_device_status)
+ gimp_devices_save (gimp, FALSE);
+
+ if (TRUE /* gui_config->save_controllers */)
+ gimp_controllers_save (gimp);
+
+ g_signal_handlers_disconnect_by_func (gimp_get_user_context (gimp),
+ gui_display_changed,
+ gimp);
+
+ gimp_displays_delete (gimp);
+
+ if (gui_config->save_accels)
+ menus_save (gimp, FALSE);
+
+ gimp_tools_save (gimp, gui_config->save_tool_options, FALSE);
+ gimp_tools_exit (gimp);
+
+ gimp_language_store_parser_clean ();
+
+ return FALSE; /* continue exiting */
+}
+
+static gboolean
+gui_exit_after_callback (Gimp *gimp,
+ gboolean force)
+{
+ if (gimp->be_verbose)
+ g_print ("EXIT: %s\n", G_STRFUNC);
+
+ g_signal_handlers_disconnect_by_func (gimp->config,
+ gui_show_help_button_notify,
+ gimp);
+ g_signal_handlers_disconnect_by_func (gimp->config,
+ gui_user_manual_notify,
+ gimp);
+ g_signal_handlers_disconnect_by_func (gimp->config,
+ gui_show_tooltips_notify,
+ gimp);
+
+ gimp_action_history_exit (gimp);
+
+ g_object_unref (image_ui_manager);
+ image_ui_manager = NULL;
+
+ g_object_unref (ui_configurer);
+ ui_configurer = NULL;
+
+ /* exit the clipboard before shutting down the GUI because it runs
+ * a whole lot of code paths. See bug #731389.
+ */
+ g_signal_handlers_disconnect_by_func (gimp,
+ G_CALLBACK (gui_clipboard_changed),
+ NULL);
+ gimp_clipboard_exit (gimp);
+
+ session_exit (gimp);
+ menus_exit (gimp);
+ actions_exit (gimp);
+ gimp_render_exit (gimp);
+
+ gimp_controllers_exit (gimp);
+ gimp_devices_exit (gimp);
+ dialogs_exit (gimp);
+ themes_exit (gimp);
+
+ g_type_class_unref (g_type_class_peek (GIMP_TYPE_COLOR_SELECT));
+
+ return FALSE; /* continue exiting */
+}
+
+static void
+gui_show_tooltips_notify (GimpGuiConfig *gui_config,
+ GParamSpec *param_spec,
+ Gimp *gimp)
+{
+ if (gui_config->show_tooltips)
+ gimp_help_enable_tooltips ();
+ else
+ gimp_help_disable_tooltips ();
+}
+
+static void
+gui_show_help_button_notify (GimpGuiConfig *gui_config,
+ GParamSpec *param_spec,
+ Gimp *gimp)
+{
+ gimp_dialogs_show_help_button (gui_config->use_help &&
+ gui_config->show_help_button);
+}
+
+static void
+gui_user_manual_notify (GimpGuiConfig *gui_config,
+ GParamSpec *param_spec,
+ Gimp *gimp)
+{
+ gimp_help_user_manual_changed (gimp);
+}
+
+static void
+gui_single_window_mode_notify (GimpGuiConfig *gui_config,
+ GParamSpec *pspec,
+ GimpUIConfigurer *ui_configurer)
+{
+ gimp_ui_configurer_configure (ui_configurer,
+ gui_config->single_window_mode);
+}
+static void
+gui_tearoff_menus_notify (GimpGuiConfig *gui_config,
+ GParamSpec *pspec,
+ GtkUIManager *manager)
+{
+ gtk_ui_manager_set_add_tearoffs (manager, gui_config->tearoff_menus);
+}
+
+static void
+gui_clipboard_changed (Gimp *gimp)
+{
+ if (gimp_get_clipboard_image (gimp))
+ gimp_clipboard_set_image (gimp, gimp_get_clipboard_image (gimp));
+ else
+ gimp_clipboard_set_buffer (gimp, gimp_get_clipboard_buffer (gimp));
+}
+
+static void
+gui_menu_show_tooltip (GimpUIManager *manager,
+ const gchar *tooltip,
+ Gimp *gimp)
+{
+ GimpContext *context = gimp_get_user_context (gimp);
+ GimpDisplay *display = gimp_context_get_display (context);
+
+ if (display)
+ {
+ GimpDisplayShell *shell = gimp_display_get_shell (display);
+ GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell);
+
+ gimp_statusbar_push (statusbar, "menu-tooltip",
+ NULL, "%s", tooltip);
+ }
+}
+
+static void
+gui_menu_hide_tooltip (GimpUIManager *manager,
+ Gimp *gimp)
+{
+ GimpContext *context = gimp_get_user_context (gimp);
+ GimpDisplay *display = gimp_context_get_display (context);
+
+ if (display)
+ {
+ GimpDisplayShell *shell = gimp_display_get_shell (display);
+ GimpStatusbar *statusbar = gimp_display_shell_get_statusbar (shell);
+
+ gimp_statusbar_pop (statusbar, "menu-tooltip");
+ }
+}
+
+static void
+gui_display_changed (GimpContext *context,
+ GimpDisplay *display,
+ Gimp *gimp)
+{
+ if (! display)
+ {
+ GimpImage *image = gimp_context_get_image (context);
+
+ if (image)
+ {
+ GList *list;
+
+ for (list = gimp_get_display_iter (gimp);
+ list;
+ list = g_list_next (list))
+ {
+ GimpDisplay *display2 = list->data;
+
+ if (gimp_display_get_image (display2) == image)
+ {
+ gimp_context_set_display (context, display2);
+
+ /* stop the emission of the original signal
+ * (the emission of the recursive signal is finished)
+ */
+ g_signal_stop_emission_by_name (context, "display-changed");
+ return;
+ }
+ }
+
+ gimp_context_set_image (context, NULL);
+ }
+ }
+
+ gimp_ui_manager_update (image_ui_manager, display);
+}
+
+typedef struct
+{
+ const gchar *path;
+ guint key;
+ GdkModifierType mods;
+}
+accelData;
+
+static void
+gui_compare_accelerator (gpointer data,
+ const gchar *accel_path,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ gboolean changed)
+{
+ accelData *accel = data;
+
+ if (accel->key == accel_key && accel->mods == accel_mods &&
+ g_strcmp0 (accel->path, accel_path))
+ {
+ g_printerr ("Actions \"%s\" and \"%s\" use the same accelerator.\n"
+ " Disabling the accelerator on \"%s\".\n",
+ accel->path, accel_path, accel_path);
+ gtk_accel_map_change_entry (accel_path, 0, 0, FALSE);
+ }
+}
+
+static void
+gui_check_unique_accelerator (gpointer data,
+ const gchar *accel_path,
+ guint accel_key,
+ GdkModifierType accel_mods,
+ gboolean changed)
+{
+ if (gtk_accelerator_valid (accel_key, accel_mods) &&
+ gui_check_action_exists (accel_path))
+ {
+ accelData accel;
+
+ accel.path = accel_path;
+ accel.key = accel_key;
+ accel.mods = accel_mods;
+
+ gtk_accel_map_foreach_unfiltered (&accel,
+ gui_compare_accelerator);
+ }
+}
+
+static gboolean
+gui_check_action_exists (const gchar *accel_path)
+{
+ GimpUIManager *manager;
+ gboolean action_exists = FALSE;
+ GList *list;
+
+ manager = gimp_ui_managers_from_name ("<Image>")->data;
+
+ for (list = gimp_ui_manager_get_action_groups (manager);
+ list;
+ list = g_list_next (list))
+ {
+ GimpActionGroup *group = list->data;
+ GList *actions = NULL;
+ GList *list2;
+
+ actions = gimp_action_group_list_actions (group);
+
+ for (list2 = actions; list2; list2 = g_list_next (list2))
+ {
+ GimpAction *action = list2->data;
+ const gchar *path = gimp_action_get_accel_path (action);
+
+ if (g_strcmp0 (path, accel_path) == 0)
+ {
+ action_exists = TRUE;
+ break;
+ }
+ }
+
+ g_list_free (actions);
+
+ if (action_exists)
+ break;
+ }
+
+ return action_exists;
+}
diff --git a/app/gui/gui.h b/app/gui/gui.h
new file mode 100644
index 0000000..4b0435d
--- /dev/null
+++ b/app/gui/gui.h
@@ -0,0 +1,30 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GUI_H__
+#define __GUI_H__
+
+
+void gui_libs_init (GOptionContext *context);
+void gui_abort (const gchar *abort_message);
+
+GimpInitStatusFunc gui_init (Gimp *gimp,
+ gboolean no_splash);
+
+gboolean gui_recover (gint n_recoveries);
+
+#endif /* __GUI_H__ */
diff --git a/app/gui/icon-themes.c b/app/gui/icon-themes.c
new file mode 100644
index 0000000..27c39eb
--- /dev/null
+++ b/app/gui/icon-themes.c
@@ -0,0 +1,250 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * icon-themes.c
+ * Copyright (C) 2015 Benoit Touchette
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gui-types.h"
+
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+
+#include "icon-themes.h"
+
+#include "gimp-intl.h"
+
+
+static void icons_apply_theme (Gimp *gimp,
+ const gchar *icon_theme_name);
+static void icons_list_icons_foreach (gpointer key,
+ gpointer value,
+ gpointer data);
+static gint icons_name_compare (const void *p1,
+ const void *p2);
+static void icons_theme_change_notify (GimpGuiConfig *config,
+ GParamSpec *pspec,
+ Gimp *gimp);
+
+
+static GHashTable *icon_themes_hash = NULL;
+
+
+void
+icon_themes_init (Gimp *gimp)
+{
+ GimpGuiConfig *config;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ config = GIMP_GUI_CONFIG (gimp->config);
+
+ icon_themes_hash = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+
+ if (config->icon_theme_path)
+ {
+ GList *path;
+ GList *list;
+
+ path = gimp_config_path_expand_to_files (config->icon_theme_path, NULL);
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ GFile *dir = list->data;
+ GFileEnumerator *enumerator;
+
+ enumerator =
+ g_file_enumerate_children (dir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator,
+ NULL, NULL)))
+ {
+ if (! g_file_info_get_is_hidden (info) &&
+ g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+ {
+ GFile *file;
+ GFile *index_theme;
+
+ file = g_file_enumerator_get_child (enumerator, info);
+
+ /* make sure there is a hicolor/index.theme file */
+ index_theme = g_file_get_child (file, "index.theme");
+
+ if (g_file_query_exists (index_theme, NULL))
+ {
+ const gchar *name;
+ gchar *basename;
+
+ name = gimp_file_get_utf8_name (file);
+ basename = g_path_get_basename (name);
+
+ if (strcmp ("hicolor", basename))
+ {
+ if (gimp->be_verbose)
+ g_print ("Adding icon theme '%s' (%s)\n",
+ basename, name);
+
+ g_hash_table_insert (icon_themes_hash, basename,
+ g_object_ref (file));
+ }
+ else
+ {
+ g_free (basename);
+ }
+ }
+
+ g_object_unref (index_theme);
+ g_object_unref (file);
+ }
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+ }
+
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+ }
+
+ g_signal_connect (config, "notify::icon-theme",
+ G_CALLBACK (icons_theme_change_notify),
+ gimp);
+
+ icons_theme_change_notify (config, NULL, gimp);
+}
+
+void
+icon_themes_exit (Gimp *gimp)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ if (icon_themes_hash)
+ {
+ g_signal_handlers_disconnect_by_func (gimp->config,
+ icons_theme_change_notify,
+ gimp);
+
+ g_hash_table_destroy (icon_themes_hash);
+ icon_themes_hash = NULL;
+ }
+}
+
+gchar **
+icon_themes_list_themes (Gimp *gimp,
+ gint *n_icon_themes)
+{
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+ g_return_val_if_fail (n_icon_themes != NULL, NULL);
+
+ *n_icon_themes = g_hash_table_size (icon_themes_hash);
+
+ if (*n_icon_themes > 0)
+ {
+ gchar **icon_themes;
+ gchar **index;
+
+ icon_themes = g_new0 (gchar *, *n_icon_themes + 1);
+
+ index = icon_themes;
+
+ g_hash_table_foreach (icon_themes_hash, icons_list_icons_foreach, &index);
+
+ qsort (icon_themes, *n_icon_themes, sizeof (gchar *), icons_name_compare);
+
+ return icon_themes;
+ }
+
+ return NULL;
+}
+
+GFile *
+icon_themes_get_theme_dir (Gimp *gimp,
+ const gchar *icon_theme_name)
+{
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+
+ if (! icon_theme_name)
+ icon_theme_name = GIMP_CONFIG_DEFAULT_ICON_THEME;
+
+ return g_hash_table_lookup (icon_themes_hash, icon_theme_name);
+}
+
+static void
+icons_apply_theme (Gimp *gimp,
+ const gchar *icon_theme_name)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ if (! icon_theme_name)
+ icon_theme_name = GIMP_CONFIG_DEFAULT_ICON_THEME;
+
+ if (gimp->be_verbose)
+ g_print ("Loading icon theme '%s'\n", icon_theme_name);
+
+ gimp_icons_set_icon_theme (icon_themes_get_theme_dir (gimp, icon_theme_name));
+}
+
+static void
+icons_list_icons_foreach (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ gchar ***index = data;
+
+ **index = g_strdup ((gchar *) key);
+
+ (*index)++;
+}
+
+static gint
+icons_name_compare (const void *p1,
+ const void *p2)
+{
+ return strcmp (* (char **) p1, * (char **) p2);
+}
+
+static void
+icons_theme_change_notify (GimpGuiConfig *config,
+ GParamSpec *pspec,
+ Gimp *gimp)
+{
+ icons_apply_theme (gimp, config->icon_theme);
+}
diff --git a/app/gui/icon-themes.h b/app/gui/icon-themes.h
new file mode 100644
index 0000000..14ecef9
--- /dev/null
+++ b/app/gui/icon-themes.h
@@ -0,0 +1,34 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * icon-themes.h
+ * Copyright (C) 2015 Benoit Touchette
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ICONS_THEMES_H__
+#define __ICONS_THEMES_H__
+
+
+void icon_themes_init (Gimp *gimp);
+void icon_themes_exit (Gimp *gimp);
+
+gchar ** icon_themes_list_themes (Gimp *gimp,
+ gint *n_themes);
+GFile * icon_themes_get_theme_dir (Gimp *gimp,
+ const gchar *theme_name);
+
+
+#endif /* __ICONS_THEMES_H__ */
diff --git a/app/gui/session.c b/app/gui/session.c
new file mode 100644
index 0000000..cfbc1c8
--- /dev/null
+++ b/app/gui/session.c
@@ -0,0 +1,486 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Session-managment stuff
+ * Copyright (C) 1998 Sven Neumann <sven@gimp.org>
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gui-types.h"
+
+#include "config/gimpconfig-file.h"
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimperror.h"
+
+#include "widgets/gimpdialogfactory.h"
+#include "widgets/gimpsessioninfo.h"
+#include "widgets/gimpwidgets-utils.h"
+
+#include "dialogs/dialogs.h"
+
+#include "session.h"
+#include "gimp-log.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ SESSION_INFO = 1,
+ HIDE_DOCKS,
+ SINGLE_WINDOW_MODE,
+ SHOW_TABS,
+ TABS_POSITION,
+ LAST_TIP_SHOWN
+};
+
+
+static GFile * session_file (Gimp *gimp);
+
+
+/* private variables */
+
+static gboolean sessionrc_deleted = FALSE;
+
+
+/* public functions */
+
+void
+session_init (Gimp *gimp)
+{
+ GFile *file;
+ GScanner *scanner;
+ GTokenType token;
+ GError *error = NULL;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ file = session_file (gimp);
+
+ scanner = gimp_scanner_new_gfile (file, &error);
+
+ if (! scanner && error->code == GIMP_CONFIG_ERROR_OPEN_ENOENT)
+ {
+ g_clear_error (&error);
+ g_object_unref (file);
+
+ file = gimp_sysconf_directory_file ("sessionrc", NULL);
+
+ scanner = gimp_scanner_new_gfile (file, NULL);
+ }
+
+ if (! scanner)
+ {
+ g_clear_error (&error);
+ g_object_unref (file);
+ return;
+ }
+
+ if (gimp->be_verbose)
+ g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file));
+
+ g_scanner_scope_add_symbol (scanner, 0, "session-info",
+ GINT_TO_POINTER (SESSION_INFO));
+ g_scanner_scope_add_symbol (scanner, 0, "hide-docks",
+ GINT_TO_POINTER (HIDE_DOCKS));
+ g_scanner_scope_add_symbol (scanner, 0, "single-window-mode",
+ GINT_TO_POINTER (SINGLE_WINDOW_MODE));
+ g_scanner_scope_add_symbol (scanner, 0, "show-tabs",
+ GINT_TO_POINTER (SHOW_TABS));
+ g_scanner_scope_add_symbol (scanner, 0, "tabs-position",
+ GINT_TO_POINTER (TABS_POSITION));
+ g_scanner_scope_add_symbol (scanner, 0, "last-tip-shown",
+ GINT_TO_POINTER (LAST_TIP_SHOWN));
+
+ token = G_TOKEN_LEFT_PAREN;
+
+ while (g_scanner_peek_next_token (scanner) == token)
+ {
+ token = g_scanner_get_next_token (scanner);
+
+ switch (token)
+ {
+ case G_TOKEN_LEFT_PAREN:
+ token = G_TOKEN_SYMBOL;
+ break;
+
+ case G_TOKEN_SYMBOL:
+ if (scanner->value.v_symbol == GINT_TO_POINTER (SESSION_INFO))
+ {
+ GimpDialogFactory *factory = NULL;
+ GimpSessionInfo *info = NULL;
+ gchar *factory_name = NULL;
+ gchar *entry_name = NULL;
+ GimpDialogFactoryEntry *entry = NULL;
+
+ token = G_TOKEN_STRING;
+
+ if (! gimp_scanner_parse_string (scanner, &factory_name))
+ break;
+
+ /* In versions <= GIMP 2.6 there was a "toolbox", a
+ * "dock", a "display" and a "toplevel" factory. These
+ * are now merged to a single gimp_dialog_factory_get_singleton (). We
+ * need the legacy name though, so keep it around.
+ */
+ factory = gimp_dialog_factory_get_singleton ();
+
+ info = gimp_session_info_new ();
+
+ /* GIMP 2.6 has the entry name as part of the
+ * session-info header, so try to get it
+ */
+ gimp_scanner_parse_string (scanner, &entry_name);
+ if (entry_name)
+ {
+ /* Previously, GimpDock was a toplevel. That is why
+ * versions <= GIMP 2.6 has "dock" as the entry name. We
+ * want "dock" to be interpreted as 'dock window'
+ * however so have some special-casing for that. When
+ * the entry name is "dock" the factory name is either
+ * "dock" or "toolbox".
+ */
+ if (strcmp (entry_name, "dock") == 0)
+ {
+ entry =
+ gimp_dialog_factory_find_entry (factory,
+ (strcmp (factory_name, "toolbox") == 0 ?
+ "gimp-toolbox-window" :
+ "gimp-dock-window"));
+ }
+ else
+ {
+ entry = gimp_dialog_factory_find_entry (factory,
+ entry_name);
+ }
+ }
+
+ /* We're done with these now */
+ g_free (factory_name);
+ g_free (entry_name);
+
+ /* We can get the factory entry either now (the GIMP <=
+ * 2.6 way), or when we deserialize (the GIMP 2.8 way)
+ */
+ if (entry)
+ {
+ gimp_session_info_set_factory_entry (info, entry);
+ }
+
+ /* Always try to deserialize */
+ if (gimp_config_deserialize (GIMP_CONFIG (info), scanner, 1, NULL))
+ {
+ /* Make sure we got a factory entry either the 2.6
+ * or 2.8 way
+ */
+ if (gimp_session_info_get_factory_entry (info))
+ {
+ GIMP_LOG (DIALOG_FACTORY,
+ "successfully parsed and added session info %p",
+ info);
+
+ gimp_dialog_factory_add_session_info (factory, info);
+ }
+ else
+ {
+ GIMP_LOG (DIALOG_FACTORY,
+ "failed to parse session info %p, not adding",
+ info);
+ }
+
+ g_object_unref (info);
+ }
+ else
+ {
+ g_object_unref (info);
+
+ /* set token to left paren to we won't set another
+ * error below, gimp_config_deserialize() already did
+ */
+ token = G_TOKEN_LEFT_PAREN;
+ goto error;
+ }
+ }
+ else if (scanner->value.v_symbol == GINT_TO_POINTER (HIDE_DOCKS))
+ {
+ gboolean hide_docks;
+
+ token = G_TOKEN_IDENTIFIER;
+
+ if (! gimp_scanner_parse_boolean (scanner, &hide_docks))
+ break;
+
+ g_object_set (gimp->config,
+ "hide-docks", hide_docks,
+ NULL);
+ }
+ else if (scanner->value.v_symbol == GINT_TO_POINTER (SINGLE_WINDOW_MODE))
+ {
+ gboolean single_window_mode;
+
+ token = G_TOKEN_IDENTIFIER;
+
+ if (! gimp_scanner_parse_boolean (scanner, &single_window_mode))
+ break;
+
+ g_object_set (gimp->config,
+ "single-window-mode", single_window_mode,
+ NULL);
+ }
+ else if (scanner->value.v_symbol == GINT_TO_POINTER (SHOW_TABS))
+ {
+ gboolean show_tabs;
+
+ token = G_TOKEN_IDENTIFIER;
+
+ if (! gimp_scanner_parse_boolean (scanner, &show_tabs))
+ break;
+
+ g_object_set (gimp->config,
+ "show-tabs", show_tabs,
+ NULL);
+ }
+ else if (scanner->value.v_symbol == GINT_TO_POINTER (TABS_POSITION))
+ {
+ gint tabs_position;
+
+ token = G_TOKEN_INT;
+
+ if (! gimp_scanner_parse_int (scanner, &tabs_position))
+ break;
+
+ g_object_set (gimp->config,
+ "tabs-position", tabs_position,
+ NULL);
+ }
+ else if (scanner->value.v_symbol == GINT_TO_POINTER (LAST_TIP_SHOWN))
+ {
+ gint last_tip_shown;
+
+ token = G_TOKEN_INT;
+
+ if (! gimp_scanner_parse_int (scanner, &last_tip_shown))
+ break;
+
+ g_object_set (gimp->config,
+ "last-tip-shown", last_tip_shown,
+ NULL);
+ }
+ token = G_TOKEN_RIGHT_PAREN;
+ break;
+
+ case G_TOKEN_RIGHT_PAREN:
+ token = G_TOKEN_LEFT_PAREN;
+ break;
+
+ default: /* do nothing */
+ break;
+ }
+ }
+
+ error:
+
+ if (token != G_TOKEN_LEFT_PAREN)
+ {
+ g_scanner_get_next_token (scanner);
+ g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
+ _("fatal parse error"), TRUE);
+ }
+
+ if (error)
+ {
+ gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
+ g_clear_error (&error);
+
+ gimp_config_file_backup_on_error (file, "sessionrc", NULL);
+ }
+
+ gimp_scanner_destroy (scanner);
+ g_object_unref (file);
+
+ dialogs_load_recent_docks (gimp);
+}
+
+void
+session_exit (Gimp *gimp)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+}
+
+void
+session_restore (Gimp *gimp,
+ GdkScreen *screen,
+ gint monitor)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ gimp_dialog_factory_restore (gimp_dialog_factory_get_singleton (),
+ screen, monitor);
+
+ /* make sure GimpImageWindow acts upon hide-docks at the right time,
+ * see bug #678043.
+ */
+ if (GIMP_GUI_CONFIG (gimp->config)->single_window_mode &&
+ GIMP_GUI_CONFIG (gimp->config)->hide_docks)
+ {
+ g_object_notify (G_OBJECT (gimp->config), "hide-docks");
+ }
+}
+
+void
+session_save (Gimp *gimp,
+ gboolean always_save)
+{
+ GimpConfigWriter *writer;
+ GFile *file;
+ GError *error = NULL;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ if (sessionrc_deleted && ! always_save)
+ return;
+
+ file = session_file (gimp);
+
+ if (gimp->be_verbose)
+ g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file));
+
+ writer =
+ gimp_config_writer_new_gfile (file,
+ TRUE,
+ "GIMP sessionrc\n\n"
+ "This file takes session-specific info "
+ "(that is info, you want to keep between "
+ "two GIMP sessions). You are not supposed "
+ "to edit it manually, but of course you "
+ "can do. The sessionrc will be entirely "
+ "rewritten every time you quit GIMP. "
+ "If this file isn't found, defaults are "
+ "used.",
+ NULL);
+ g_object_unref (file);
+
+ if (!writer)
+ return;
+
+ gimp_dialog_factory_save (gimp_dialog_factory_get_singleton (), writer);
+ gimp_config_writer_linefeed (writer);
+
+ gimp_config_writer_open (writer, "hide-docks");
+ gimp_config_writer_identifier (writer,
+ GIMP_GUI_CONFIG (gimp->config)->hide_docks ?
+ "yes" : "no");
+ gimp_config_writer_close (writer);
+
+ gimp_config_writer_open (writer, "single-window-mode");
+ gimp_config_writer_identifier (writer,
+ GIMP_GUI_CONFIG (gimp->config)->single_window_mode ?
+ "yes" : "no");
+ gimp_config_writer_close (writer);
+
+ gimp_config_writer_open (writer, "show-tabs");
+ gimp_config_writer_printf (writer,
+ GIMP_GUI_CONFIG (gimp->config)->show_tabs ?
+ "yes" : "no");
+ gimp_config_writer_close (writer);
+
+ gimp_config_writer_open (writer, "tabs-position");
+ gimp_config_writer_printf (writer, "%d",
+ GIMP_GUI_CONFIG (gimp->config)->tabs_position);
+ gimp_config_writer_close (writer);
+
+ gimp_config_writer_open (writer, "last-tip-shown");
+ gimp_config_writer_printf (writer, "%d",
+ GIMP_GUI_CONFIG (gimp->config)->last_tip_shown);
+ gimp_config_writer_close (writer);
+
+ if (! gimp_config_writer_finish (writer, "end of sessionrc", &error))
+ {
+ gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
+ g_clear_error (&error);
+ }
+
+ dialogs_save_recent_docks (gimp);
+
+ sessionrc_deleted = FALSE;
+}
+
+gboolean
+session_clear (Gimp *gimp,
+ GError **error)
+{
+ GFile *file;
+ GError *my_error = NULL;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ file = session_file (gimp);
+
+ if (! g_file_delete (file, NULL, &my_error) &&
+ my_error->code != G_IO_ERROR_NOT_FOUND)
+ {
+ success = FALSE;
+
+ g_set_error (error, GIMP_ERROR, GIMP_FAILED,
+ _("Deleting \"%s\" failed: %s"),
+ gimp_file_get_utf8_name (file), my_error->message);
+ }
+ else
+ {
+ sessionrc_deleted = TRUE;
+ }
+
+ g_clear_error (&my_error);
+ g_object_unref (file);
+
+ return success;
+}
+
+
+static GFile *
+session_file (Gimp *gimp)
+{
+ const gchar *basename;
+ gchar *filename;
+ GFile *file;
+
+ basename = g_getenv ("GIMP_TESTING_SESSIONRC_NAME");
+ if (! basename)
+ basename = "sessionrc";
+
+ if (gimp->session_name)
+ filename = g_strconcat (basename, ".", gimp->session_name, NULL);
+ else
+ filename = g_strdup (basename);
+
+ file = gimp_directory_file (filename, NULL);
+
+ g_free (filename);
+
+ return file;
+}
diff --git a/app/gui/session.h b/app/gui/session.h
new file mode 100644
index 0000000..aff1145
--- /dev/null
+++ b/app/gui/session.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SESSION_H__
+#define __SESSION_H__
+
+
+void session_init (Gimp *gimp);
+void session_exit (Gimp *gimp);
+
+void session_restore (Gimp *gimp,
+ GdkScreen *screen,
+ gint monitor);
+void session_save (Gimp *gimp,
+ gboolean always_save);
+
+gboolean session_clear (Gimp *gimp,
+ GError **error);
+
+
+#endif /* __SESSION_H__ */
diff --git a/app/gui/splash.c b/app/gui/splash.c
new file mode 100644
index 0000000..aec664e
--- /dev/null
+++ b/app/gui/splash.c
@@ -0,0 +1,736 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gui-types.h"
+
+#include "widgets/gimpwidgets-utils.h"
+
+#include "splash.h"
+
+#include "gimp-intl.h"
+
+
+#define MEASURE_UPPER "1235678901234567890"
+#define MEASURE_LOWER "12356789012345678901234567890"
+
+
+typedef struct
+{
+ GtkWidget *window;
+ GtkWidget *area;
+ gint width;
+ gint height;
+ GtkWidget *progress;
+ GdkColor color;
+ PangoLayout *upper;
+ gint upper_x;
+ gint upper_y;
+ PangoLayout *lower;
+ gint lower_x;
+ gint lower_y;
+
+ gdouble percentage;
+ gchar *text1;
+ gchar *text2;
+
+ /* debug timer */
+ GTimer *timer;
+ gdouble last_time;
+} GimpSplash;
+
+static GimpSplash *splash = NULL;
+
+
+static void splash_position_layouts (GimpSplash *splash,
+ const gchar *text1,
+ const gchar *text2,
+ GdkRectangle *area);
+static gboolean splash_area_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpSplash *splash);
+static void splash_rectangle_union (GdkRectangle *dest,
+ PangoRectangle *pango_rect,
+ gint offset_x,
+ gint offset_y);
+static gboolean splash_average_text_area (GimpSplash *splash,
+ GdkPixbuf *pixbuf,
+ GdkColor *color);
+
+static GdkPixbufAnimation *
+ splash_image_load (gint max_width,
+ gint max_height,
+ gboolean be_verbose);
+static GdkPixbufAnimation *
+ splash_image_load_from_path (const gchar *filename,
+ gint max_width,
+ gint max_height,
+ gboolean be_verbose);
+static GdkPixbufAnimation *
+ splash_image_load_from_file (GFile *file,
+ gint max_width,
+ gint max_height,
+ gboolean be_verbose);
+static GdkPixbufAnimation *
+ splash_image_pick_from_dirs (GList *dirs,
+ gint max_width,
+ gint max_height,
+ gboolean be_verbose);
+
+static void splash_timer_elapsed (void);
+
+
+/* public functions */
+
+void
+splash_create (gboolean be_verbose,
+ GdkScreen *screen,
+ gint monitor)
+{
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GdkPixbufAnimation *pixbuf;
+ PangoRectangle ink;
+ gint max_width;
+ gint max_height;
+
+ g_return_if_fail (splash == NULL);
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ max_width = gdk_screen_get_width (screen) / 2;
+ max_height = gdk_screen_get_height (screen) / 2;
+ pixbuf = splash_image_load (max_width, max_height, be_verbose);
+
+ if (! pixbuf)
+ return;
+
+ splash = g_slice_new0 (GimpSplash);
+
+ splash->window =
+ g_object_new (GTK_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ "type-hint", GDK_WINDOW_TYPE_HINT_SPLASHSCREEN,
+ "title", _("GIMP Startup"),
+ "role", "gimp-startup",
+ "screen", screen,
+ "window-position", GTK_WIN_POS_CENTER,
+ "resizable", FALSE,
+ NULL);
+
+ /* Don't remove this call, it's necessary to remove decorations on Windows
+ * (which is the natural state of splash-screens). Looks like the
+ * GDK_WINDOW_TYPE_HINT_SPLASHSCREEN hint is not used on some platforms.
+ */
+ gtk_window_set_decorated (GTK_WINDOW (splash->window), FALSE);
+
+ g_signal_connect_swapped (splash->window, "delete-event",
+ G_CALLBACK (exit),
+ GINT_TO_POINTER (0));
+
+ splash->width = MIN (gdk_pixbuf_animation_get_width (pixbuf),
+ gdk_screen_get_width (screen));
+ splash->height = MIN (gdk_pixbuf_animation_get_height (pixbuf),
+ gdk_screen_get_height (screen));
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
+ gtk_container_add (GTK_CONTAINER (splash->window), frame);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ splash->area = gtk_image_new_from_animation (pixbuf);
+ gtk_box_pack_start (GTK_BOX (vbox), splash->area, TRUE, TRUE, 0);
+ gtk_widget_show (splash->area);
+
+ gtk_widget_set_size_request (splash->area, splash->width, splash->height);
+
+ /* create the pango layouts */
+ splash->upper = gtk_widget_create_pango_layout (splash->area,
+ MEASURE_UPPER);
+ pango_layout_get_pixel_extents (splash->upper, &ink, NULL);
+
+ if (splash->width > 4 * ink.width)
+ gimp_pango_layout_set_scale (splash->upper, PANGO_SCALE_X_LARGE);
+ else if (splash->width > 3 * ink.width)
+ gimp_pango_layout_set_scale (splash->upper, PANGO_SCALE_LARGE);
+ else if (splash->width > 2 * ink.width)
+ gimp_pango_layout_set_scale (splash->upper, PANGO_SCALE_MEDIUM);
+ else
+ gimp_pango_layout_set_scale (splash->upper, PANGO_SCALE_SMALL);
+
+ splash->lower = gtk_widget_create_pango_layout (splash->area,
+ MEASURE_LOWER);
+ pango_layout_get_pixel_extents (splash->lower, &ink, NULL);
+
+ if (splash->width > 4 * ink.width)
+ gimp_pango_layout_set_scale (splash->lower, PANGO_SCALE_LARGE);
+ else if (splash->width > 3 * ink.width)
+ gimp_pango_layout_set_scale (splash->lower, PANGO_SCALE_MEDIUM);
+ else if (splash->width > 2 * ink.width)
+ gimp_pango_layout_set_scale (splash->lower, PANGO_SCALE_SMALL);
+ else
+ gimp_pango_layout_set_scale (splash->lower, PANGO_SCALE_X_SMALL);
+
+ /* this sets the initial layout positions */
+ splash_position_layouts (splash, "", "", NULL);
+
+ splash_average_text_area (splash,
+ gdk_pixbuf_animation_get_static_image (pixbuf),
+ &splash->color);
+
+ g_object_unref (pixbuf);
+
+ g_signal_connect_after (splash->area, "expose-event",
+ G_CALLBACK (splash_area_expose),
+ splash);
+
+ /* add a progress bar */
+ splash->progress = gtk_progress_bar_new ();
+ gtk_box_pack_end (GTK_BOX (vbox), splash->progress, FALSE, FALSE, 0);
+ gtk_widget_show (splash->progress);
+
+ gtk_widget_show (splash->window);
+
+ if (FALSE)
+ splash->timer = g_timer_new ();
+}
+
+void
+splash_destroy (void)
+{
+ if (! splash)
+ return;
+
+ gtk_widget_destroy (splash->window);
+
+ g_object_unref (splash->upper);
+ g_object_unref (splash->lower);
+
+ g_free (splash->text1);
+ g_free (splash->text2);
+
+ if (splash->timer)
+ g_timer_destroy (splash->timer);
+
+ g_slice_free (GimpSplash, splash);
+ splash = NULL;
+}
+
+void
+splash_update (const gchar *text1,
+ const gchar *text2,
+ gdouble percentage)
+{
+ static GdkRectangle prev_expose = { 0, 0, 0, 0 };
+ GdkRectangle expose = { 0, 0, 0, 0 };
+
+ g_return_if_fail (percentage >= 0.0 && percentage <= 1.0);
+
+ if (! splash)
+ return;
+
+ splash_position_layouts (splash, text1, text2, &expose);
+ gdk_rectangle_union (&expose, &prev_expose, &expose);
+
+ if (expose.width > 0 && expose.height > 0)
+ gtk_widget_queue_draw_area (splash->area,
+ expose.x, expose.y,
+ expose.width, expose.height);
+
+ prev_expose = expose;
+
+ if ((text1 == NULL || ! g_strcmp0 (text1, splash->text1)) &&
+ (text2 == NULL || ! g_strcmp0 (text2, splash->text2)) &&
+ percentage == splash->percentage)
+ {
+ if (text1)
+ {
+ g_free (splash->text1);
+ splash->text1 = g_strdup (text1);
+ }
+
+ if (text2)
+ {
+ g_free (splash->text2);
+ splash->text2 = g_strdup (text2);
+ }
+
+ gtk_progress_bar_pulse (GTK_PROGRESS_BAR (splash->progress));
+ }
+ else
+ {
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (splash->progress),
+ percentage);
+ }
+
+ splash->percentage = percentage;
+
+ if (splash->timer)
+ splash_timer_elapsed ();
+
+ if (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+
+/* private functions */
+
+static gboolean
+splash_area_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpSplash *splash)
+{
+ cairo_t *cr = gdk_cairo_create (event->window);
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ gdk_cairo_set_source_color (cr, &splash->color);
+
+ cairo_move_to (cr, splash->upper_x, splash->upper_y);
+ pango_cairo_show_layout (cr, splash->upper);
+
+ cairo_move_to (cr, splash->lower_x, splash->lower_y);
+ pango_cairo_show_layout (cr, splash->lower);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+/* area returns the union of the previous and new ink rectangles */
+static void
+splash_position_layouts (GimpSplash *splash,
+ const gchar *text1,
+ const gchar *text2,
+ GdkRectangle *area)
+{
+ PangoRectangle upper_ink;
+ PangoRectangle lower_ink;
+ gint text_height = 0;
+
+ if (text1)
+ {
+ pango_layout_get_pixel_extents (splash->upper, &upper_ink, NULL);
+
+ if (area)
+ splash_rectangle_union (area, &upper_ink,
+ splash->upper_x, splash->upper_y);
+
+ pango_layout_set_text (splash->upper, text1, -1);
+ pango_layout_get_pixel_extents (splash->upper,
+ &upper_ink, NULL);
+
+ splash->upper_x = (splash->width - upper_ink.width) / 2;
+ text_height += upper_ink.height;
+ }
+
+ if (text2)
+ {
+ pango_layout_get_pixel_extents (splash->lower, &lower_ink, NULL);
+
+ if (area)
+ splash_rectangle_union (area, &lower_ink,
+ splash->lower_x, splash->lower_y);
+
+ pango_layout_set_text (splash->lower, text2, -1);
+ pango_layout_get_pixel_extents (splash->lower,
+ &lower_ink, NULL);
+
+ splash->lower_x = (splash->width - lower_ink.width) / 2;
+ text_height += lower_ink.height;
+ }
+
+ /* For pretty printing, let's say we want at least double space. */
+ text_height *= 2;
+
+ /* The ordinates are computed in 2 steps, because we are first
+ * checking the minimal height needed for text (text_height).
+ *
+ * Ideally we are printing in the bottom quarter of the splash image,
+ * with well centered positions. But if this zone appears to be too
+ * small, we will end up using this previously computed text_height
+ * instead. Since splash images are designed to have text in the lower
+ * quarter, this may end up a bit uglier, but at least top and bottom
+ * texts won't overlay each other.
+ */
+ if (text1)
+ {
+ splash->upper_y = MIN (splash->height - text_height,
+ splash->height * 13 / 16 -
+ upper_ink.height / 2);
+
+ if (area)
+ splash_rectangle_union (area, &upper_ink,
+ splash->upper_x, splash->upper_y);
+ }
+
+ if (text2)
+ {
+ splash->lower_y = ((splash->height + splash->upper_y) / 2 -
+ lower_ink.height / 2);
+
+ if (area)
+ splash_rectangle_union (area, &lower_ink,
+ splash->lower_x, splash->lower_y);
+ }
+}
+
+static void
+splash_rectangle_union (GdkRectangle *dest,
+ PangoRectangle *pango_rect,
+ gint offset_x,
+ gint offset_y)
+{
+ GdkRectangle rect;
+
+ rect.x = pango_rect->x + offset_x;
+ rect.y = pango_rect->y + offset_y;
+ rect.width = pango_rect->width;
+ rect.height = pango_rect->height;
+
+ if (dest->width > 0 && dest->height > 0)
+ gdk_rectangle_union (dest, &rect, dest);
+ else
+ *dest = rect;
+}
+
+/* This function chooses a gray value for the text color, based on
+ * the average luminance of the text area of the splash image.
+ */
+static gboolean
+splash_average_text_area (GimpSplash *splash,
+ GdkPixbuf *pixbuf,
+ GdkColor *color)
+{
+ const guchar *pixels;
+ gint rowstride;
+ gint channels;
+ gint luminance = 0;
+ guint sum[3] = { 0, 0, 0 };
+ GdkRectangle image = { 0, 0, 0, 0 };
+ GdkRectangle area = { 0, 0, 0, 0 };
+
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
+ g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8, FALSE);
+
+ image.width = gdk_pixbuf_get_width (pixbuf);
+ image.height = gdk_pixbuf_get_height (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ channels = gdk_pixbuf_get_n_channels (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ splash_position_layouts (splash, MEASURE_UPPER, MEASURE_LOWER, &area);
+ splash_position_layouts (splash, "", "", NULL);
+
+ if (gdk_rectangle_intersect (&image, &area, &area))
+ {
+ const gint count = area.width * area.height;
+ gint x, y;
+
+ pixels += area.x * channels;
+ pixels += area.y * rowstride;
+
+ for (y = 0; y < area.height; y++)
+ {
+ const guchar *src = pixels;
+
+ for (x = 0; x < area.width; x++)
+ {
+ sum[0] += src[0];
+ sum[1] += src[1];
+ sum[2] += src[2];
+
+ src += channels;
+ }
+
+ pixels += rowstride;
+ }
+
+ luminance = GIMP_RGB_LUMINANCE (sum[0] / count,
+ sum[1] / count,
+ sum[2] / count);
+
+ luminance = CLAMP0255 (luminance > 127 ?
+ luminance - 223 : luminance + 223);
+
+ }
+
+ color->red = color->green = color->blue = (luminance << 8 | luminance);
+
+ return gdk_colormap_alloc_color (gtk_widget_get_colormap (splash->area),
+ color, FALSE, TRUE);
+}
+
+static GdkPixbufAnimation *
+splash_image_load (gint max_width,
+ gint max_height,
+ gboolean be_verbose)
+{
+ GdkPixbufAnimation *animation = NULL;
+ gchar *filename;
+ GFile *file;
+ GList *list;
+
+ /* File "gimp-splash.png" in personal configuration directory. */
+ filename = gimp_personal_rc_file ("gimp-splash.png");
+ animation = splash_image_load_from_path (filename,
+ max_width, max_height,
+ be_verbose);
+ g_free (filename);
+ if (animation)
+ return animation;
+
+ /* Random image under splashes/ directory in personal config dir. */
+ filename = gimp_personal_rc_file ("splashes");
+ file = g_file_new_for_path (filename);
+ g_free (filename);
+ list = NULL;
+ list = g_list_prepend (list, file);
+ animation = splash_image_pick_from_dirs (list,
+ max_width, max_height,
+ be_verbose);
+ g_list_free_full (list, g_object_unref);
+ if (animation)
+ return animation;
+
+ /* Release splash image. */
+ filename = g_build_filename (gimp_data_directory (),
+ "images", "gimp-splash.png", NULL);
+ animation = splash_image_load_from_path (filename,
+ max_width, max_height,
+ be_verbose);
+ g_free (filename);
+ if (animation)
+ return animation;
+
+ /* Random release image in installed splashes/ directory. */
+ filename = g_build_filename (gimp_data_directory (), "splashes", NULL);
+ file = g_file_new_for_path (filename);
+ g_free (filename);
+ list = NULL;
+ list = g_list_prepend (list, file);
+ animation = splash_image_pick_from_dirs (list,
+ max_width, max_height,
+ be_verbose);
+ g_list_free_full (list, g_object_unref);
+
+ return animation;
+}
+
+static GdkPixbufAnimation *
+splash_image_load_from_path (const gchar *filename,
+ gint max_width,
+ gint max_height,
+ gboolean be_verbose)
+{
+ GdkPixbufAnimation *animation;
+ GFile *file;
+
+ file = g_file_new_for_path (filename);
+ animation = splash_image_load_from_file (file,
+ max_width, max_height,
+ be_verbose);
+ g_object_unref (file);
+
+ return animation;
+}
+
+static GdkPixbufAnimation *
+splash_image_load_from_file (GFile *file,
+ gint max_width,
+ gint max_height,
+ gboolean be_verbose)
+{
+ GdkPixbufAnimation *animation = NULL;
+ GFileInfo *info;
+ GFileInputStream *input;
+ gboolean is_svg = FALSE;
+
+ if (be_verbose)
+ {
+ gchar *path;
+
+ path = g_file_get_path (file);
+
+ g_printerr ("Trying splash '%s' ... ", path);
+
+ g_free (path);
+ }
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ if (info)
+ {
+ const gchar *content_type;
+
+ content_type = g_file_info_get_content_type (info);
+ if (content_type)
+ {
+ gchar *mime_type;
+
+ mime_type = g_content_type_get_mime_type (content_type);
+ if (mime_type)
+ {
+ if (g_strcmp0 (mime_type, "image/svg+xml") == 0)
+ {
+ /* We want to treat vector images differently than
+ * pixel images. We only scale down bitmaps, but we
+ * don't try to scale them up.
+ * On the other hand, we can always scale down and up
+ * vector images so that they end up in an ideal size
+ * in all cases.
+ */
+ is_svg = TRUE;
+ }
+ g_free (mime_type);
+ }
+ }
+ g_object_unref (info);
+ }
+
+ input = g_file_read (file, NULL, NULL);
+ if (input)
+ {
+ animation = gdk_pixbuf_animation_new_from_stream (G_INPUT_STREAM (input),
+ NULL, NULL);
+ g_object_unref (input);
+ }
+
+ /* FIXME Right now, we only try to scale static images.
+ * Animated images may end up bigger than the expected max dimensions.
+ */
+ if (animation && gdk_pixbuf_animation_is_static_image (animation) &&
+ (gdk_pixbuf_animation_get_width (animation) > max_width ||
+ gdk_pixbuf_animation_get_height (animation) > max_height ||
+ is_svg))
+ {
+ GdkPixbuf *pixbuf;
+
+ input = g_file_read (file, NULL, NULL);
+ pixbuf = gdk_pixbuf_new_from_stream_at_scale (G_INPUT_STREAM (input),
+ max_width, max_height,
+ TRUE, NULL, NULL);
+ g_object_unref (input);
+ if (pixbuf)
+ {
+ GdkPixbufSimpleAnim *simple_anim = NULL;
+
+ simple_anim = gdk_pixbuf_simple_anim_new (gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ 1.0);
+ if (simple_anim)
+ {
+ gdk_pixbuf_simple_anim_add_frame (simple_anim, pixbuf);
+
+ g_object_unref (animation);
+ animation = GDK_PIXBUF_ANIMATION (simple_anim);
+ }
+ g_object_unref (pixbuf);
+ }
+ }
+
+ if (be_verbose)
+ g_printerr (animation ? "OK\n" : "failed\n");
+
+ return animation;
+}
+
+static GdkPixbufAnimation *
+splash_image_pick_from_dirs (GList *dirs,
+ gint max_width,
+ gint max_height,
+ gboolean be_verbose)
+{
+ GdkPixbufAnimation *animation = NULL;
+ GList *splashes = NULL;
+ GList *iter;
+
+ for (iter = dirs; iter; iter = iter->next)
+ {
+ GFileEnumerator *enumerator;
+
+ enumerator = g_file_enumerate_children (iter->data,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)))
+ {
+ GFile *child;
+
+ child = g_file_enumerator_get_child (enumerator, info);
+ if (g_file_query_file_type (child,
+ G_FILE_QUERY_INFO_NONE,
+ NULL) == G_FILE_TYPE_REGULAR)
+ splashes = g_list_prepend (splashes, child);
+ else
+ g_object_unref (child);
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+ }
+
+ if (splashes)
+ {
+ gint32 i = g_random_int_range (0, g_list_length (splashes));
+
+ animation = splash_image_load_from_file (g_list_nth_data (splashes, i),
+ max_width, max_height,
+ be_verbose);
+ g_list_free_full (splashes, (GDestroyNotify) g_object_unref);
+ }
+
+ return animation;
+}
+
+static void
+splash_timer_elapsed (void)
+{
+ gdouble elapsed = g_timer_elapsed (splash->timer, NULL);
+
+ g_printerr ("%8g %8g - %s %g%% - %s\n",
+ elapsed,
+ elapsed - splash->last_time,
+ splash->text1 ? splash->text1 : "",
+ splash->percentage * 100.0,
+ splash->text2 ? splash->text2 : "");
+
+ splash->last_time = elapsed;
+}
diff --git a/app/gui/splash.h b/app/gui/splash.h
new file mode 100644
index 0000000..a44f9b3
--- /dev/null
+++ b/app/gui/splash.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SPLASH_H__
+#define __SPLASH_H__
+
+
+void splash_create (gboolean be_verbose,
+ GdkScreen *screen,
+ gint monitor);
+void splash_destroy (void);
+
+void splash_update (const gchar *label1,
+ const gchar *label2,
+ gdouble percentage);
+
+
+#endif /* __SPLASH_H__ */
diff --git a/app/gui/themes.c b/app/gui/themes.c
new file mode 100644
index 0000000..0307094
--- /dev/null
+++ b/app/gui/themes.c
@@ -0,0 +1,535 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <gegl.h>
+#ifdef GDK_DISABLE_DEPRECATED
+#undef GDK_DISABLE_DEPRECATED
+#endif
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gui-types.h"
+
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+
+#include "themes.h"
+
+#include "gimp-intl.h"
+
+
+/* local function prototypes */
+
+static void themes_write_style (GimpGuiConfig *config,
+ GOutputStream *output,
+ GError **error);
+static void themes_apply_theme (Gimp *gimp,
+ GimpGuiConfig *config);
+static void themes_list_themes_foreach (gpointer key,
+ gpointer value,
+ gpointer data);
+static gint themes_name_compare (const void *p1,
+ const void *p2);
+static void themes_theme_change_notify (GimpGuiConfig *config,
+ GParamSpec *pspec,
+ Gimp *gimp);
+
+static void themes_fix_pixbuf_style (void);
+static void themes_draw_pixbuf_layout (GtkStyle *style,
+ GdkWindow *window,
+ GtkStateType state_type,
+ gboolean use_text,
+ GdkRectangle *area,
+ GtkWidget *widget,
+ const gchar *detail,
+ gint x,
+ gint y,
+ PangoLayout *layout);
+
+/* private variables */
+
+static GHashTable *themes_hash = NULL;
+static GtkStyleClass *pixbuf_style_class = NULL;
+
+
+/* public functions */
+
+void
+themes_init (Gimp *gimp)
+{
+ GimpGuiConfig *config;
+ gchar *themerc;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ config = GIMP_GUI_CONFIG (gimp->config);
+
+ themes_hash = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+
+ if (config->theme_path)
+ {
+ GList *path;
+ GList *list;
+
+ path = gimp_config_path_expand_to_files (config->theme_path, NULL);
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ GFile *dir = list->data;
+ GFileEnumerator *enumerator;
+
+ enumerator =
+ g_file_enumerate_children (dir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator,
+ NULL, NULL)))
+ {
+ if (! g_file_info_get_is_hidden (info) &&
+ g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+ {
+ GFile *file;
+ const gchar *name;
+ gchar *basename;
+
+ file = g_file_enumerator_get_child (enumerator, info);
+ name = gimp_file_get_utf8_name (file);
+
+ basename = g_path_get_basename (name);
+
+ if (gimp->be_verbose)
+ g_print ("Adding theme '%s' (%s)\n",
+ basename, name);
+
+ g_hash_table_insert (themes_hash, basename, file);
+ }
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+ }
+
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+ }
+
+ themes_apply_theme (gimp, config);
+
+ themerc = gimp_personal_rc_file ("themerc");
+ gtk_rc_parse (themerc);
+ g_free (themerc);
+
+ themes_fix_pixbuf_style ();
+
+ g_signal_connect (config, "notify::theme",
+ G_CALLBACK (themes_theme_change_notify),
+ gimp);
+ g_signal_connect (config, "notify::compact-sliders",
+ G_CALLBACK (themes_theme_change_notify),
+ gimp);
+}
+
+void
+themes_exit (Gimp *gimp)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ if (themes_hash)
+ {
+ g_signal_handlers_disconnect_by_func (gimp->config,
+ themes_theme_change_notify,
+ gimp);
+
+ g_hash_table_destroy (themes_hash);
+ themes_hash = NULL;
+ }
+
+ g_clear_pointer (&pixbuf_style_class, g_type_class_unref);
+}
+
+gchar **
+themes_list_themes (Gimp *gimp,
+ gint *n_themes)
+{
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+ g_return_val_if_fail (n_themes != NULL, NULL);
+
+ *n_themes = g_hash_table_size (themes_hash);
+
+ if (*n_themes > 0)
+ {
+ gchar **themes;
+ gchar **index;
+
+ themes = g_new0 (gchar *, *n_themes + 1);
+
+ index = themes;
+
+ g_hash_table_foreach (themes_hash, themes_list_themes_foreach, &index);
+
+ qsort (themes, *n_themes, sizeof (gchar *), themes_name_compare);
+
+ return themes;
+ }
+
+ return NULL;
+}
+
+GFile *
+themes_get_theme_dir (Gimp *gimp,
+ const gchar *theme_name)
+{
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+
+ if (! theme_name)
+ theme_name = GIMP_CONFIG_DEFAULT_THEME;
+
+ return g_hash_table_lookup (themes_hash, theme_name);
+}
+
+GFile *
+themes_get_theme_file (Gimp *gimp,
+ const gchar *first_component,
+ ...)
+{
+ GimpGuiConfig *gui_config;
+ GFile *file;
+ const gchar *component;
+ va_list args;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+ g_return_val_if_fail (first_component != NULL, NULL);
+
+ gui_config = GIMP_GUI_CONFIG (gimp->config);
+
+ file = g_object_ref (themes_get_theme_dir (gimp, gui_config->theme));
+ component = first_component;
+
+ va_start (args, first_component);
+
+ do
+ {
+ GFile *tmp = g_file_get_child (file, component);
+ g_object_unref (file);
+ file = tmp;
+ }
+ while ((component = va_arg (args, gchar *)));
+
+ va_end (args);
+
+ if (! g_file_query_exists (file, NULL))
+ {
+ g_object_unref (file);
+
+ file = g_object_ref (themes_get_theme_dir (gimp, NULL));
+ component = first_component;
+
+ va_start (args, first_component);
+
+ do
+ {
+ GFile *tmp = g_file_get_child (file, component);
+ g_object_unref (file);
+ file = tmp;
+ }
+ while ((component = va_arg (args, gchar *)));
+
+ va_end (args);
+ }
+
+ return file;
+}
+
+
+/* private functions */
+
+static void
+themes_write_style (GimpGuiConfig *config,
+ GOutputStream *output,
+ GError **error)
+{
+ if (! *error)
+ {
+ g_output_stream_printf (
+ output, NULL, NULL, error,
+ "style \"gimp-spin-scale-style\"\n"
+ "{\n"
+ " GimpSpinScale::compact = %d\n"
+ "}\n"
+ "\n"
+ "class \"GimpSpinScale\" style \"gimp-spin-scale-style\"\n"
+ "\n",
+ config->compact_sliders);
+ }
+}
+
+static void
+themes_apply_theme (Gimp *gimp,
+ GimpGuiConfig *config)
+{
+ GFile *themerc;
+ GOutputStream *output;
+ GError *error = NULL;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+
+ themerc = gimp_directory_file ("themerc", NULL);
+
+ if (gimp->be_verbose)
+ g_print ("Writing '%s'\n", gimp_file_get_utf8_name (themerc));
+
+ output = G_OUTPUT_STREAM (g_file_replace (themerc,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, &error));
+ if (! output)
+ {
+ gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ GFile *theme_dir = themes_get_theme_dir (gimp, config->theme);
+ GFile *gtkrc_user;
+ GSList *gtkrc_files = NULL;
+ GSList *iter;
+
+ if (theme_dir)
+ {
+ gtkrc_files = g_slist_prepend (
+ gtkrc_files,
+ g_file_get_child (theme_dir, "gtkrc"));
+ }
+ else
+ {
+ /* get the hardcoded default theme gtkrc */
+ gtkrc_files = g_slist_prepend (
+ gtkrc_files,
+ g_file_new_for_path (gimp_gtkrc ()));
+ }
+
+ gtkrc_files = g_slist_prepend (
+ gtkrc_files,
+ gimp_sysconf_directory_file ("gtkrc", NULL));
+
+ gtkrc_user = gimp_directory_file ("gtkrc", NULL);
+ gtkrc_files = g_slist_prepend (
+ gtkrc_files,
+ gtkrc_user);
+
+ gtkrc_files = g_slist_reverse (gtkrc_files);
+
+ g_output_stream_printf (
+ output, NULL, NULL, &error,
+ "# GIMP themerc\n"
+ "#\n"
+ "# This file is written on GIMP startup and on every theme change.\n"
+ "# It is NOT supposed to be edited manually. Edit your personal\n"
+ "# gtkrc file instead (%s).\n"
+ "\n",
+ gimp_file_get_utf8_name (gtkrc_user));
+
+ themes_write_style (config, output, &error);
+
+ for (iter = gtkrc_files; ! error && iter; iter = g_slist_next (iter))
+ {
+ GFile *file = iter->data;
+
+ if (g_file_query_exists (file, NULL))
+ {
+ gchar *path;
+ gchar *esc_path;
+
+ path = g_file_get_path (file);
+ esc_path = g_strescape (path, NULL);
+ g_free (path);
+
+ g_output_stream_printf (
+ output, NULL, NULL, &error,
+ "include \"%s\"\n",
+ esc_path);
+
+ g_free (esc_path);
+ }
+ }
+
+ if (! error)
+ {
+ g_output_stream_printf (
+ output, NULL, NULL, &error,
+ "\n"
+ "# end of themerc\n");
+ }
+
+ if (error)
+ {
+ GCancellable *cancellable = g_cancellable_new ();
+
+ gimp_message (gimp, NULL, GIMP_MESSAGE_ERROR,
+ _("Error writing '%s': %s"),
+ gimp_file_get_utf8_name (themerc), error->message);
+ g_clear_error (&error);
+
+ /* Cancel the overwrite initiated by g_file_replace(). */
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+ }
+ else if (! g_output_stream_close (output, NULL, &error))
+ {
+ gimp_message (gimp, NULL, GIMP_MESSAGE_ERROR,
+ _("Error closing '%s': %s"),
+ gimp_file_get_utf8_name (themerc), error->message);
+ g_clear_error (&error);
+ }
+
+ g_slist_free_full (gtkrc_files, g_object_unref);
+ g_object_unref (output);
+ }
+
+ g_object_unref (themerc);
+}
+
+static void
+themes_list_themes_foreach (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ gchar ***index = data;
+
+ **index = g_strdup ((gchar *) key);
+
+ (*index)++;
+}
+
+static gint
+themes_name_compare (const void *p1,
+ const void *p2)
+{
+ return strcmp (* (char **) p1, * (char **) p2);
+}
+
+static void
+themes_theme_change_notify (GimpGuiConfig *config,
+ GParamSpec *pspec,
+ Gimp *gimp)
+{
+ themes_apply_theme (gimp, config);
+
+ gtk_rc_reparse_all ();
+
+ themes_fix_pixbuf_style ();
+}
+
+static void
+themes_fix_pixbuf_style (void)
+{
+ /* This is a "quick'n dirty" trick to get appropriate colors for
+ * themes in GTK+2, and in particular dark themes which would display
+ * insensitive items with a barely readable layout.
+ *
+ * This piece of code partly duplicates code from GTK+2 (slightly
+ * modified to get readable insensitive items) and will likely have to
+ * be removed for GIMP 3.
+ *
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=770424
+ */
+
+ if (! pixbuf_style_class)
+ {
+ GType type = g_type_from_name ("PixbufStyle");
+
+ if (type)
+ {
+ pixbuf_style_class = g_type_class_ref (type);
+
+ if (pixbuf_style_class)
+ pixbuf_style_class->draw_layout = themes_draw_pixbuf_layout;
+ }
+ }
+}
+
+static void
+themes_draw_pixbuf_layout (GtkStyle *style,
+ GdkWindow *window,
+ GtkStateType state_type,
+ gboolean use_text,
+ GdkRectangle *area,
+ GtkWidget *widget,
+ const gchar *detail,
+ gint x,
+ gint y,
+ PangoLayout *layout)
+{
+ GdkGC *gc;
+
+ gc = use_text ? style->text_gc[state_type] : style->fg_gc[state_type];
+
+ if (area)
+ gdk_gc_set_clip_rectangle (gc, area);
+
+ if (state_type == GTK_STATE_INSENSITIVE)
+ {
+ GdkGC *copy = gdk_gc_new (window);
+ GdkGCValues orig;
+ GdkColor fore;
+ guint16 r, g, b;
+
+ gdk_gc_copy (copy, gc);
+ gdk_gc_get_values (gc, &orig);
+
+ r = 0x40 + (((orig.foreground.pixel >> 16) & 0xff) >> 1);
+ g = 0x40 + (((orig.foreground.pixel >> 8) & 0xff) >> 1);
+ b = 0x40 + (((orig.foreground.pixel >> 0) & 0xff) >> 1);
+
+ fore.pixel = (r << 16) | (g << 8) | b;
+ fore.red = r * 257;
+ fore.green = g * 257;
+ fore.blue = b * 257;
+
+ gdk_gc_set_foreground (copy, &fore);
+ gdk_draw_layout (window, copy, x, y, layout);
+
+ g_object_unref (copy);
+ }
+ else
+ {
+ gdk_draw_layout (window, gc, x, y, layout);
+ }
+
+ if (area)
+ gdk_gc_set_clip_rectangle (gc, NULL);
+}
diff --git a/app/gui/themes.h b/app/gui/themes.h
new file mode 100644
index 0000000..0b7e74f
--- /dev/null
+++ b/app/gui/themes.h
@@ -0,0 +1,34 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __THEMES_H__
+#define __THEMES_H__
+
+
+void themes_init (Gimp *gimp);
+void themes_exit (Gimp *gimp);
+
+gchar ** themes_list_themes (Gimp *gimp,
+ gint *n_themes);
+GFile * themes_get_theme_dir (Gimp *gimp,
+ const gchar *theme_name);
+GFile * themes_get_theme_file (Gimp *gimp,
+ const gchar *first_component,
+ ...) G_GNUC_NULL_TERMINATED;
+
+
+#endif /* __THEMES_H__ */