diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
commit | 3c57dd931145d43f2b0aef96c4d178135956bf91 (patch) | |
tree | 3de698981e9f0cc2c4f9569b19a5f3595e741f6b /app/gui | |
parent | Initial commit. (diff) | |
download | gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip |
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/gui')
-rw-r--r-- | app/gui/Makefile.am | 80 | ||||
-rw-r--r-- | app/gui/Makefile.in | 1018 | ||||
-rw-r--r-- | app/gui/dbus-service.xml | 31 | ||||
-rw-r--r-- | app/gui/gimpdbusservice-generated.c | 1721 | ||||
-rw-r--r-- | app/gui/gimpdbusservice-generated.h | 282 | ||||
-rw-r--r-- | app/gui/gimpdbusservice.c | 457 | ||||
-rw-r--r-- | app/gui/gimpdbusservice.h | 69 | ||||
-rw-r--r-- | app/gui/gimpuiconfigurer.c | 622 | ||||
-rw-r--r-- | app/gui/gimpuiconfigurer.h | 57 | ||||
-rw-r--r-- | app/gui/gui-message.c | 501 | ||||
-rw-r--r-- | app/gui/gui-message.h | 29 | ||||
-rw-r--r-- | app/gui/gui-types.h | 27 | ||||
-rw-r--r-- | app/gui/gui-unique.c | 442 | ||||
-rw-r--r-- | app/gui/gui-unique.h | 31 | ||||
-rw-r--r-- | app/gui/gui-vtable.c | 934 | ||||
-rw-r--r-- | app/gui/gui-vtable.h | 31 | ||||
-rw-r--r-- | app/gui/gui.c | 1077 | ||||
-rw-r--r-- | app/gui/gui.h | 30 | ||||
-rw-r--r-- | app/gui/icon-themes.c | 250 | ||||
-rw-r--r-- | app/gui/icon-themes.h | 34 | ||||
-rw-r--r-- | app/gui/session.c | 486 | ||||
-rw-r--r-- | app/gui/session.h | 35 | ||||
-rw-r--r-- | app/gui/splash.c | 736 | ||||
-rw-r--r-- | app/gui/splash.h | 32 | ||||
-rw-r--r-- | app/gui/themes.c | 535 | ||||
-rw-r--r-- | app/gui/themes.h | 34 |
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 (¶mv[0], GIMP_DBUS_SERVICE_TYPE_UI); + g_value_set_object (¶mv[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 (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[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 (¶mv[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 (¶mv[n], GIMP_DBUS_SERVICE_TYPE_UI); + g_value_set_object (¶mv[n++], skeleton); + g_value_init (¶mv[n], G_TYPE_DBUS_METHOD_INVOCATION); + g_value_set_object (¶mv[n++], invocation); + if (info->pass_fdlist) + { +#ifdef G_OS_UNIX + g_value_init (¶mv[n], G_TYPE_UNIX_FD_LIST); + g_value_set_object (¶mv[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 (¶mv[n], G_TYPE_VARIANT); + g_value_set_variant (¶mv[n], child); + n++; + } + else + g_dbus_gvariant_to_gvalue (child, ¶mv[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 (¶mv[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__ */ |