summaryrefslogtreecommitdiffstats
path: root/app/propgui
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--app/propgui/Makefile.am63
-rw-r--r--app/propgui/Makefile.in1060
-rw-r--r--app/propgui/gimppropgui-channel-mixer.c141
-rw-r--r--app/propgui/gimppropgui-channel-mixer.h35
-rw-r--r--app/propgui/gimppropgui-color-balance.c149
-rw-r--r--app/propgui/gimppropgui-color-balance.h35
-rw-r--r--app/propgui/gimppropgui-color-rotate.c260
-rw-r--r--app/propgui/gimppropgui-color-rotate.h35
-rw-r--r--app/propgui/gimppropgui-color-to-alpha.c140
-rw-r--r--app/propgui/gimppropgui-color-to-alpha.h35
-rw-r--r--app/propgui/gimppropgui-convolution-matrix.c309
-rw-r--r--app/propgui/gimppropgui-convolution-matrix.h35
-rw-r--r--app/propgui/gimppropgui-diffraction-patterns.c105
-rw-r--r--app/propgui/gimppropgui-diffraction-patterns.h35
-rw-r--r--app/propgui/gimppropgui-eval.c1039
-rw-r--r--app/propgui/gimppropgui-eval.h36
-rw-r--r--app/propgui/gimppropgui-focus-blur.c246
-rw-r--r--app/propgui/gimppropgui-focus-blur.h36
-rw-r--r--app/propgui/gimppropgui-generic.c377
-rw-r--r--app/propgui/gimppropgui-generic.h36
-rw-r--r--app/propgui/gimppropgui-hue-saturation.c288
-rw-r--r--app/propgui/gimppropgui-hue-saturation.h35
-rw-r--r--app/propgui/gimppropgui-motion-blur-circular.c151
-rw-r--r--app/propgui/gimppropgui-motion-blur-circular.h35
-rw-r--r--app/propgui/gimppropgui-motion-blur-linear.c145
-rw-r--r--app/propgui/gimppropgui-motion-blur-linear.h35
-rw-r--r--app/propgui/gimppropgui-motion-blur-zoom.c146
-rw-r--r--app/propgui/gimppropgui-motion-blur-zoom.h35
-rw-r--r--app/propgui/gimppropgui-newsprint.c444
-rw-r--r--app/propgui/gimppropgui-newsprint.h35
-rw-r--r--app/propgui/gimppropgui-panorama-projection.c144
-rw-r--r--app/propgui/gimppropgui-panorama-projection.h35
-rw-r--r--app/propgui/gimppropgui-recursive-transform.c334
-rw-r--r--app/propgui/gimppropgui-recursive-transform.h35
-rw-r--r--app/propgui/gimppropgui-shadows-highlights.c122
-rw-r--r--app/propgui/gimppropgui-shadows-highlights.h35
-rw-r--r--app/propgui/gimppropgui-spiral.c239
-rw-r--r--app/propgui/gimppropgui-spiral.h35
-rw-r--r--app/propgui/gimppropgui-supernova.c144
-rw-r--r--app/propgui/gimppropgui-supernova.h35
-rw-r--r--app/propgui/gimppropgui-utils.c221
-rw-r--r--app/propgui/gimppropgui-utils.h32
-rw-r--r--app/propgui/gimppropgui-vignette.c202
-rw-r--r--app/propgui/gimppropgui-vignette.h36
-rw-r--r--app/propgui/gimppropgui.c702
-rw-r--r--app/propgui/gimppropgui.h60
-rw-r--r--app/propgui/propgui-types.h145
47 files changed, 8112 insertions, 0 deletions
diff --git a/app/propgui/Makefile.am b/app/propgui/Makefile.am
new file mode 100644
index 0000000..0f64ee7
--- /dev/null
+++ b/app/propgui/Makefile.am
@@ -0,0 +1,63 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = \
+ -DISO_CODES_LOCATION=\"$(ISO_CODES_LOCATION)\" \
+ -DISO_CODES_LOCALEDIR=\"$(ISO_CODES_LOCALEDIR)\" \
+ -DG_LOG_DOMAIN=\"Gimp-PropGUI\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(GEGL_CFLAGS) \
+ $(GTK_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = libapppropgui.a
+
+libapppropgui_a_SOURCES = \
+ propgui-types.h \
+ \
+ gimppropgui.c \
+ gimppropgui.h \
+ gimppropgui-channel-mixer.c \
+ gimppropgui-channel-mixer.h \
+ gimppropgui-color-balance.c \
+ gimppropgui-color-balance.h \
+ gimppropgui-color-rotate.c \
+ gimppropgui-color-rotate.h \
+ gimppropgui-color-to-alpha.c \
+ gimppropgui-color-to-alpha.h \
+ gimppropgui-convolution-matrix.c \
+ gimppropgui-convolution-matrix.h \
+ gimppropgui-diffraction-patterns.c \
+ gimppropgui-diffraction-patterns.h \
+ gimppropgui-eval.c \
+ gimppropgui-eval.h \
+ gimppropgui-focus-blur.c \
+ gimppropgui-focus-blur.h \
+ gimppropgui-generic.c \
+ gimppropgui-generic.h \
+ gimppropgui-hue-saturation.c \
+ gimppropgui-hue-saturation.h \
+ gimppropgui-motion-blur-circular.c \
+ gimppropgui-motion-blur-circular.h \
+ gimppropgui-motion-blur-linear.c \
+ gimppropgui-motion-blur-linear.h \
+ gimppropgui-motion-blur-zoom.c \
+ gimppropgui-motion-blur-zoom.h \
+ gimppropgui-newsprint.c \
+ gimppropgui-newsprint.h \
+ gimppropgui-panorama-projection.c \
+ gimppropgui-panorama-projection.h \
+ gimppropgui-recursive-transform.c \
+ gimppropgui-recursive-transform.h \
+ gimppropgui-shadows-highlights.c \
+ gimppropgui-shadows-highlights.h \
+ gimppropgui-spiral.c \
+ gimppropgui-spiral.h \
+ gimppropgui-supernova.c \
+ gimppropgui-supernova.h \
+ gimppropgui-utils.c \
+ gimppropgui-utils.h \
+ gimppropgui-vignette.c \
+ gimppropgui-vignette.h
diff --git a/app/propgui/Makefile.in b/app/propgui/Makefile.in
new file mode 100644
index 0000000..f9f1a21
--- /dev/null
+++ b/app/propgui/Makefile.in
@@ -0,0 +1,1060 @@
+# Makefile.in generated by automake 1.16.2 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/propgui
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.m4 \
+ $(top_srcdir)/m4macros/intltool.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(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 =
+libapppropgui_a_AR = $(AR) $(ARFLAGS)
+libapppropgui_a_LIBADD =
+am_libapppropgui_a_OBJECTS = gimppropgui.$(OBJEXT) \
+ gimppropgui-channel-mixer.$(OBJEXT) \
+ gimppropgui-color-balance.$(OBJEXT) \
+ gimppropgui-color-rotate.$(OBJEXT) \
+ gimppropgui-color-to-alpha.$(OBJEXT) \
+ gimppropgui-convolution-matrix.$(OBJEXT) \
+ gimppropgui-diffraction-patterns.$(OBJEXT) \
+ gimppropgui-eval.$(OBJEXT) gimppropgui-focus-blur.$(OBJEXT) \
+ gimppropgui-generic.$(OBJEXT) \
+ gimppropgui-hue-saturation.$(OBJEXT) \
+ gimppropgui-motion-blur-circular.$(OBJEXT) \
+ gimppropgui-motion-blur-linear.$(OBJEXT) \
+ gimppropgui-motion-blur-zoom.$(OBJEXT) \
+ gimppropgui-newsprint.$(OBJEXT) \
+ gimppropgui-panorama-projection.$(OBJEXT) \
+ gimppropgui-recursive-transform.$(OBJEXT) \
+ gimppropgui-shadows-highlights.$(OBJEXT) \
+ gimppropgui-spiral.$(OBJEXT) gimppropgui-supernova.$(OBJEXT) \
+ gimppropgui-utils.$(OBJEXT) gimppropgui-vignette.$(OBJEXT)
+libapppropgui_a_OBJECTS = $(am_libapppropgui_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)/gimppropgui-channel-mixer.Po \
+ ./$(DEPDIR)/gimppropgui-color-balance.Po \
+ ./$(DEPDIR)/gimppropgui-color-rotate.Po \
+ ./$(DEPDIR)/gimppropgui-color-to-alpha.Po \
+ ./$(DEPDIR)/gimppropgui-convolution-matrix.Po \
+ ./$(DEPDIR)/gimppropgui-diffraction-patterns.Po \
+ ./$(DEPDIR)/gimppropgui-eval.Po \
+ ./$(DEPDIR)/gimppropgui-focus-blur.Po \
+ ./$(DEPDIR)/gimppropgui-generic.Po \
+ ./$(DEPDIR)/gimppropgui-hue-saturation.Po \
+ ./$(DEPDIR)/gimppropgui-motion-blur-circular.Po \
+ ./$(DEPDIR)/gimppropgui-motion-blur-linear.Po \
+ ./$(DEPDIR)/gimppropgui-motion-blur-zoom.Po \
+ ./$(DEPDIR)/gimppropgui-newsprint.Po \
+ ./$(DEPDIR)/gimppropgui-panorama-projection.Po \
+ ./$(DEPDIR)/gimppropgui-recursive-transform.Po \
+ ./$(DEPDIR)/gimppropgui-shadows-highlights.Po \
+ ./$(DEPDIR)/gimppropgui-spiral.Po \
+ ./$(DEPDIR)/gimppropgui-supernova.Po \
+ ./$(DEPDIR)/gimppropgui-utils.Po \
+ ./$(DEPDIR)/gimppropgui-vignette.Po ./$(DEPDIR)/gimppropgui.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 = $(libapppropgui_a_SOURCES)
+DIST_SOURCES = $(libapppropgui_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_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_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@
+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@
+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@
+AM_CPPFLAGS = \
+ -DISO_CODES_LOCATION=\"$(ISO_CODES_LOCATION)\" \
+ -DISO_CODES_LOCALEDIR=\"$(ISO_CODES_LOCALEDIR)\" \
+ -DG_LOG_DOMAIN=\"Gimp-PropGUI\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(GEGL_CFLAGS) \
+ $(GTK_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = libapppropgui.a
+libapppropgui_a_SOURCES = \
+ propgui-types.h \
+ \
+ gimppropgui.c \
+ gimppropgui.h \
+ gimppropgui-channel-mixer.c \
+ gimppropgui-channel-mixer.h \
+ gimppropgui-color-balance.c \
+ gimppropgui-color-balance.h \
+ gimppropgui-color-rotate.c \
+ gimppropgui-color-rotate.h \
+ gimppropgui-color-to-alpha.c \
+ gimppropgui-color-to-alpha.h \
+ gimppropgui-convolution-matrix.c \
+ gimppropgui-convolution-matrix.h \
+ gimppropgui-diffraction-patterns.c \
+ gimppropgui-diffraction-patterns.h \
+ gimppropgui-eval.c \
+ gimppropgui-eval.h \
+ gimppropgui-focus-blur.c \
+ gimppropgui-focus-blur.h \
+ gimppropgui-generic.c \
+ gimppropgui-generic.h \
+ gimppropgui-hue-saturation.c \
+ gimppropgui-hue-saturation.h \
+ gimppropgui-motion-blur-circular.c \
+ gimppropgui-motion-blur-circular.h \
+ gimppropgui-motion-blur-linear.c \
+ gimppropgui-motion-blur-linear.h \
+ gimppropgui-motion-blur-zoom.c \
+ gimppropgui-motion-blur-zoom.h \
+ gimppropgui-newsprint.c \
+ gimppropgui-newsprint.h \
+ gimppropgui-panorama-projection.c \
+ gimppropgui-panorama-projection.h \
+ gimppropgui-recursive-transform.c \
+ gimppropgui-recursive-transform.h \
+ gimppropgui-shadows-highlights.c \
+ gimppropgui-shadows-highlights.h \
+ gimppropgui-spiral.c \
+ gimppropgui-spiral.h \
+ gimppropgui-supernova.c \
+ gimppropgui-supernova.h \
+ gimppropgui-utils.c \
+ gimppropgui-utils.h \
+ gimppropgui-vignette.c \
+ gimppropgui-vignette.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu app/propgui/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu app/propgui/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)
+
+libapppropgui.a: $(libapppropgui_a_OBJECTS) $(libapppropgui_a_DEPENDENCIES) $(EXTRA_libapppropgui_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libapppropgui.a
+ $(AM_V_AR)$(libapppropgui_a_AR) libapppropgui.a $(libapppropgui_a_OBJECTS) $(libapppropgui_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libapppropgui.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-channel-mixer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-color-balance.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-color-rotate.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-color-to-alpha.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-convolution-matrix.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-diffraction-patterns.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-eval.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-focus-blur.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-generic.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-hue-saturation.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-motion-blur-circular.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-motion-blur-linear.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-motion-blur-zoom.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-newsprint.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-panorama-projection.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-recursive-transform.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-shadows-highlights.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-spiral.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-supernova.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-utils.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui-vignette.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropgui.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: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ 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:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimppropgui-channel-mixer.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-color-balance.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-color-rotate.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-color-to-alpha.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-convolution-matrix.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-diffraction-patterns.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-eval.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-focus-blur.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-generic.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-hue-saturation.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-motion-blur-circular.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-motion-blur-linear.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-motion-blur-zoom.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-newsprint.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-panorama-projection.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-recursive-transform.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-shadows-highlights.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-spiral.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-supernova.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-utils.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-vignette.Po
+ -rm -f ./$(DEPDIR)/gimppropgui.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)/gimppropgui-channel-mixer.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-color-balance.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-color-rotate.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-color-to-alpha.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-convolution-matrix.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-diffraction-patterns.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-eval.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-focus-blur.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-generic.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-hue-saturation.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-motion-blur-circular.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-motion-blur-linear.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-motion-blur-zoom.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-newsprint.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-panorama-projection.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-recursive-transform.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-shadows-highlights.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-spiral.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-supernova.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-utils.Po
+ -rm -f ./$(DEPDIR)/gimppropgui-vignette.Po
+ -rm -f ./$(DEPDIR)/gimppropgui.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: install-am 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
+
+
+# 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/propgui/gimppropgui-channel-mixer.c b/app/propgui/gimppropgui-channel-mixer.c
new file mode 100644
index 0000000..df48d7c
--- /dev/null
+++ b/app/propgui/gimppropgui-channel-mixer.c
@@ -0,0 +1,141 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-channel-mixer.c
+ * Copyright (C) 2002-2014 Michael Natterer <mitch@gimp.org>
+ * 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 "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-channel-mixer.h"
+
+#include "gimp-intl.h"
+
+
+GtkWidget *
+_gimp_prop_gui_new_channel_mixer (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *checkbox;
+ GtkWidget *scale;
+ const gchar *label;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+
+ frame = gimp_frame_new (_("Red channel"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ scale = gimp_prop_widget_new (config, "rr-gain",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "rg-gain",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "rb-gain",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+
+ frame = gimp_frame_new (_("Green channel"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ scale = gimp_prop_widget_new (config, "gr-gain",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "gg-gain",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "gb-gain",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+
+ frame = gimp_frame_new (_("Blue channel"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ scale = gimp_prop_widget_new (config, "br-gain",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "bg-gain",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "bb-gain",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+
+ checkbox = gimp_prop_widget_new (config, "preserve-luminosity",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (main_vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show (checkbox);
+
+ return main_vbox;
+}
diff --git a/app/propgui/gimppropgui-channel-mixer.h b/app/propgui/gimppropgui-channel-mixer.h
new file mode 100644
index 0000000..a44f99b
--- /dev/null
+++ b/app/propgui/gimppropgui-channel-mixer.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-channel-mixer.h
+ *
+ * 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_PROP_GUI_CHANNEL_MIXER_H__
+#define __GIMP_PROP_GUI_CHANNEL_MIXER_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_channel_mixer (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_CHANNEL_MIXER_H__ */
diff --git a/app/propgui/gimppropgui-color-balance.c b/app/propgui/gimppropgui-color-balance.c
new file mode 100644
index 0000000..18127ae
--- /dev/null
+++ b/app/propgui/gimppropgui-color-balance.c
@@ -0,0 +1,149 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-color-balance.c
+ *
+ * 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 "propgui-types.h"
+
+#include "operations/gimpcolorbalanceconfig.h"
+
+#include "core/gimpcontext.h"
+
+#include "widgets/gimppropwidgets.h"
+#include "widgets/gimpspinscale.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-color-balance.h"
+
+#include "gimp-intl.h"
+
+
+static void
+create_levels_scale (GObject *config,
+ const gchar *property_name,
+ const gchar *left,
+ const gchar *right,
+ GtkWidget *table,
+ gint col)
+{
+ GtkWidget *label;
+ GtkWidget *scale;
+
+ label = gtk_label_new (left);
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, col, col + 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ scale = gimp_prop_spin_scale_new (config, property_name,
+ NULL, 0.01, 0.1, 0);
+ gimp_spin_scale_set_label (GIMP_SPIN_SCALE (scale), NULL);
+ gimp_prop_widget_set_factor (scale, 100.0, 0.0, 0.0, 1);
+ gtk_table_attach_defaults (GTK_TABLE (table), scale, 1, 2, col, col + 1);
+ gtk_widget_show (scale);
+
+ label = gtk_label_new (right);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, col, col + 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_color_balance (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkWidget *frame;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+
+ frame = gimp_prop_enum_radio_frame_new (config, "range",
+ _("Select Range to Adjust"),
+ 0, 0);
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ frame = gimp_frame_new (_("Adjust Color Levels"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* The table containing sliders */
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 2);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ create_levels_scale (config, "cyan-red",
+ _("Cyan"), _("Red"),
+ table, 0);
+
+ create_levels_scale (config, "magenta-green",
+ _("Magenta"), _("Green"),
+ table, 1);
+
+ create_levels_scale (config, "yellow-blue",
+ _("Yellow"), _("Blue"),
+ table, 2);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ button = gtk_button_new_with_mnemonic (_("R_eset Range"));
+ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_color_balance_config_reset_range),
+ config);
+
+ button = gimp_prop_check_button_new (config,
+ "preserve-luminosity",
+ _("Preserve _luminosity"));
+ gtk_box_pack_end (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ return main_vbox;
+}
diff --git a/app/propgui/gimppropgui-color-balance.h b/app/propgui/gimppropgui-color-balance.h
new file mode 100644
index 0000000..ecbee73
--- /dev/null
+++ b/app/propgui/gimppropgui-color-balance.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-color-balance.h
+ *
+ * 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_PROP_GUI_COLOR_BALANCE_H__
+#define __GIMP_PROP_GUI_COLOR_BALANCE_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_color_balance (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_COLOR_BALANCE_H__ */
diff --git a/app/propgui/gimppropgui-color-rotate.c b/app/propgui/gimppropgui-color-rotate.c
new file mode 100644
index 0000000..1461084
--- /dev/null
+++ b/app/propgui/gimppropgui-color-rotate.c
@@ -0,0 +1,260 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-color-rotate.c
+ * Copyright (C) 2002-2014 Michael Natterer <mitch@gimp.org>
+ * 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 "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "widgets/gimppropwidgets.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-color-rotate.h"
+#include "gimppropgui-generic.h"
+
+#include "gimp-intl.h"
+
+
+static void
+invert_segment_clicked (GtkWidget *button,
+ GtkWidget *dial)
+{
+ gdouble alpha, beta;
+ gboolean clockwise;
+
+ g_object_get (dial,
+ "alpha", &alpha,
+ "beta", &beta,
+ "clockwise-delta", &clockwise,
+ NULL);
+
+ g_object_set (dial,
+ "alpha", beta,
+ "beta", alpha,
+ "clockwise-delta", ! clockwise,
+ NULL);
+}
+
+static void
+select_all_clicked (GtkWidget *button,
+ GtkWidget *dial)
+{
+ gdouble alpha, beta;
+ gboolean clockwise;
+
+ g_object_get (dial,
+ "alpha", &alpha,
+ "clockwise-delta", &clockwise,
+ NULL);
+
+ beta = alpha - (clockwise ? -1 : 1) * 0.00001;
+
+ if (beta < 0)
+ beta += 2 * G_PI;
+
+ if (beta > 2 * G_PI)
+ beta -= 2 * G_PI;
+
+ g_object_set (dial,
+ "beta", beta,
+ NULL);
+}
+
+static GtkWidget *
+gimp_prop_angle_range_box_new (GObject *config,
+ const gchar *alpha_property_name,
+ const gchar *beta_property_name,
+ const gchar *clockwise_property_name)
+{
+ GtkWidget *main_hbox;
+ GtkWidget *vbox;
+ GtkWidget *scale;
+ GtkWidget *hbox;
+ GtkWidget *button;
+ GtkWidget *invert_button;
+ GtkWidget *all_button;
+ GtkWidget *dial;
+
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+ gtk_box_pack_start (GTK_BOX (main_hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ scale = gimp_prop_spin_scale_new (config, alpha_property_name, NULL,
+ 1.0, 15.0, 2);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (scale), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_spin_scale_new (config, beta_property_name, NULL,
+ 1.0, 15.0, 2);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (scale), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ button = gimp_prop_check_button_new (config, clockwise_property_name,
+ _("Clockwise"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ invert_button = gtk_button_new_with_label (_("Invert Range"));
+ gtk_box_pack_start (GTK_BOX (hbox), invert_button, TRUE, TRUE, 0);
+ gtk_widget_show (invert_button);
+
+ all_button = gtk_button_new_with_label (_("Select All"));
+ gtk_box_pack_start (GTK_BOX (hbox), all_button, TRUE, TRUE, 0);
+ gtk_widget_show (all_button);
+
+ dial = gimp_prop_angle_range_dial_new (config,
+ alpha_property_name,
+ beta_property_name,
+ clockwise_property_name);
+ gtk_box_pack_start (GTK_BOX (main_hbox), dial, FALSE, FALSE, 0);
+ gtk_widget_show (dial);
+
+ g_signal_connect (invert_button, "clicked",
+ G_CALLBACK (invert_segment_clicked),
+ dial);
+
+ g_signal_connect (all_button, "clicked",
+ G_CALLBACK (select_all_clicked),
+ dial);
+
+ return main_hbox;
+}
+
+static GtkWidget *
+gimp_prop_polar_box_new (GObject *config,
+ const gchar *angle_property_name,
+ const gchar *radius_property_name)
+{
+ GtkWidget *main_hbox;
+ GtkWidget *vbox;
+ GtkWidget *scale;
+ GtkWidget *polar;
+
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+ gtk_box_pack_start (GTK_BOX (main_hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ scale = gimp_prop_spin_scale_new (config, angle_property_name, NULL,
+ 1.0, 15.0, 2);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (scale), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_spin_scale_new (config, radius_property_name, NULL,
+ 1.0, 15.0, 2);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ polar = gimp_prop_polar_new (config,
+ angle_property_name,
+ radius_property_name);
+ gtk_box_pack_start (GTK_BOX (main_hbox), polar, FALSE, FALSE, 0);
+ gtk_widget_show (polar);
+
+ return main_hbox;
+}
+
+GtkWidget *
+_gimp_prop_gui_new_color_rotate (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *box;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+
+ frame = gimp_frame_new (_("Source Range"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ box = gimp_prop_angle_range_box_new (config,
+ param_specs[1]->name,
+ param_specs[2]->name,
+ param_specs[0]->name);
+ gtk_container_add (GTK_CONTAINER (frame), box);
+ gtk_widget_show (box);
+
+ frame = gimp_frame_new (_("Destination Range"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ box = gimp_prop_angle_range_box_new (config,
+ param_specs[4]->name,
+ param_specs[5]->name,
+ param_specs[3]->name);
+ gtk_container_add (GTK_CONTAINER (frame), box);
+ gtk_widget_show (box);
+
+ frame = gimp_frame_new (_("Gray Handling"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ box = _gimp_prop_gui_new_generic (config,
+ param_specs + 6, 2,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 0);
+ gtk_widget_show (box);
+
+ box = gimp_prop_polar_box_new (config,
+ param_specs[8]->name,
+ param_specs[9]->name);
+ gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 0);
+ gtk_widget_show (box);
+
+ return main_vbox;
+}
diff --git a/app/propgui/gimppropgui-color-rotate.h b/app/propgui/gimppropgui-color-rotate.h
new file mode 100644
index 0000000..a69770d
--- /dev/null
+++ b/app/propgui/gimppropgui-color-rotate.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-color-rotate.h
+ *
+ * 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_PROP_GUI_COLOR_ROTATE_H__
+#define __GIMP_PROP_GUI_COLOR_ROTATE_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_color_rotate (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_COLOR_ROTATE_H__ */
diff --git a/app/propgui/gimppropgui-color-to-alpha.c b/app/propgui/gimppropgui-color-to-alpha.c
new file mode 100644
index 0000000..0918bf9
--- /dev/null
+++ b/app/propgui/gimppropgui-color-to-alpha.c
@@ -0,0 +1,140 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-color-to-alpha.c
+ * Copyright (C) 2017 Ell
+ *
+ * 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-color-to-alpha.h"
+#include "gimppropgui-generic.h"
+
+#include "gimp-intl.h"
+
+
+static void
+threshold_picked (GObject *config,
+ gpointer identifier,
+ gdouble x,
+ gdouble y,
+ const Babl *sample_format,
+ const GimpRGB *picked_color)
+{
+ GimpRGB *color;
+ gdouble threshold = 0.0;
+
+ g_object_get (config,
+ "color", &color,
+ NULL);
+
+ threshold = MAX (threshold, fabs (picked_color->r - color->r));
+ threshold = MAX (threshold, fabs (picked_color->g - color->g));
+ threshold = MAX (threshold, fabs (picked_color->b - color->b));
+
+ g_object_set (config,
+ identifier, threshold,
+ NULL);
+
+ g_free (color);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_color_to_alpha (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *vbox;
+ GtkWidget *button;
+ GtkWidget *hbox;
+ GtkWidget *scale;
+ const gchar *label;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+
+ button = _gimp_prop_gui_new_generic (config, param_specs, 1,
+ area, context, create_picker_func, NULL,
+ creator);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ scale = gimp_prop_widget_new (config, "transparency-threshold",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
+ gtk_widget_show (scale);
+
+ if (create_picker_func)
+ {
+ button = create_picker_func (creator,
+ "transparency-threshold",
+ GIMP_ICON_COLOR_PICKER_GRAY,
+ _("Pick farthest full-transparency color"),
+ /* pick_abyss = */ FALSE,
+ (GimpPickerCallback) threshold_picked,
+ config);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+ }
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ scale = gimp_prop_widget_new (config, "opacity-threshold",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
+ gtk_widget_show (scale);
+
+ if (create_picker_func)
+ {
+ button = create_picker_func (creator,
+ "opacity-threshold",
+ GIMP_ICON_COLOR_PICKER_GRAY,
+ _("Pick nearest full-opacity color"),
+ /* pick_abyss = */ FALSE,
+ (GimpPickerCallback) threshold_picked,
+ config);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+ }
+
+ return vbox;
+}
diff --git a/app/propgui/gimppropgui-color-to-alpha.h b/app/propgui/gimppropgui-color-to-alpha.h
new file mode 100644
index 0000000..af5a325
--- /dev/null
+++ b/app/propgui/gimppropgui-color-to-alpha.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-color-to-alpha.h
+ *
+ * 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_PROP_GUI_COLOR_TO_ALPHA_H__
+#define __GIMP_PROP_GUI_COLOR_TO_ALPHA_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_color_to_alpha (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_COLOR_TO_ALPHA_H__ */
diff --git a/app/propgui/gimppropgui-convolution-matrix.c b/app/propgui/gimppropgui-convolution-matrix.c
new file mode 100644
index 0000000..7a5635e
--- /dev/null
+++ b/app/propgui/gimppropgui-convolution-matrix.c
@@ -0,0 +1,309 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-convolution-matrix.c
+ * Copyright (C) 2002-2014 Michael Natterer <mitch@gimp.org>
+ * 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 "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-convolution-matrix.h"
+#include "gimppropgui-generic.h"
+
+#include "gimp-intl.h"
+
+
+static const gchar *
+convolution_matrix_prop_name (gint x,
+ gint y)
+{
+ static const gchar * const prop_names[5][5] = {
+ {"a1", "a2", "a3", "a4", "a5"},
+ {"b1", "b2", "b3", "b4", "b5"},
+ {"c1", "c2", "c3", "c4", "c5"},
+ {"d1", "d2", "d3", "d4", "d5"},
+ {"e1", "e2", "e3", "e4", "e5"}};
+
+ return prop_names[x][y];
+}
+
+static void
+convolution_matrix_rotate_flip (GtkWidget *button,
+ GObject *config)
+{
+ gint rotate = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
+ "convolution-matrix-rotate"));
+ gint flip = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
+ "convolution-matrix-flip"));
+ gint x, y;
+
+ while (rotate--)
+ {
+ for (y = 0; y < 2; y++)
+ {
+ for (x = y; x < 4 - y; x++)
+ {
+ gint i;
+ gdouble temp;
+
+ g_object_get (config,
+ convolution_matrix_prop_name (x, y), &temp,
+ NULL);
+
+ for (i = 0; i < 4; i++)
+ {
+ gint next_x, next_y;
+ gdouble val;
+
+ next_x = 4 - y;
+ next_y = x;
+
+ if (i < 3)
+ {
+ g_object_get (config,
+ convolution_matrix_prop_name (next_x, next_y), &val,
+ NULL);
+ }
+ else
+ {
+ val = temp;
+ }
+
+ g_object_set (config,
+ convolution_matrix_prop_name (x, y), val,
+ NULL);
+
+ x = next_x;
+ y = next_y;
+ }
+ }
+ }
+ }
+
+ while (flip--)
+ {
+ for (y = 0; y < 5; y++)
+ {
+ for (x = 0; x < 2; x++)
+ {
+ gdouble val1, val2;
+
+ g_object_get (config,
+ convolution_matrix_prop_name (x, y), &val1,
+ NULL);
+ g_object_get (config,
+ convolution_matrix_prop_name (4 - x, y), &val2,
+ NULL);
+
+ g_object_set (config,
+ convolution_matrix_prop_name (x, y), val2,
+ NULL);
+ g_object_set (config,
+ convolution_matrix_prop_name (4 - x, y), val1,
+ NULL);
+ }
+ }
+ }
+}
+
+GtkWidget *
+_gimp_prop_gui_new_convolution_matrix (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *hbox;
+ GtkWidget *scale;
+ GtkWidget *vbox2;
+ const gchar *label;
+ gint x, y;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ /* matrix */
+
+ table = gtk_table_new (5, 5, TRUE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 2);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ for (y = 0; y < 5; y++)
+ {
+ for (x = 0; x < 5; x++)
+ {
+ GtkWidget *spin;
+
+ spin = gimp_prop_spin_button_new (config,
+ convolution_matrix_prop_name (x, y),
+ 1.0, 10.0, 2);
+ gtk_entry_set_width_chars (GTK_ENTRY (spin), 8);
+ gtk_table_attach (GTK_TABLE (table), spin,
+ x, x + 1, y, y + 1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND,
+ 0, 0);
+ gtk_widget_show (spin);
+ }
+ }
+
+ /* rotate / flip buttons */
+ {
+ typedef struct
+ {
+ const gchar *tooltip;
+ const gchar *icon_name;
+ gint rotate;
+ gint flip;
+ } ButtonInfo;
+
+ gint i;
+ const ButtonInfo buttons[] = {
+ {
+ .tooltip = _("Rotate matrix 90° counter-clockwise"),
+ .icon_name = GIMP_ICON_OBJECT_ROTATE_270,
+ .rotate = 1,
+ .flip = 0
+ },
+ {
+ .tooltip = _("Rotate matrix 90° clockwise"),
+ .icon_name = GIMP_ICON_OBJECT_ROTATE_90,
+ .rotate = 3,
+ .flip = 0
+ },
+ {
+ .tooltip = _("Flip matrix horizontally"),
+ .icon_name = GIMP_ICON_OBJECT_FLIP_HORIZONTAL,
+ .rotate = 0,
+ .flip = 1
+ },
+ {
+ .tooltip = _("Flip matrix vertically"),
+ .icon_name = GIMP_ICON_OBJECT_FLIP_VERTICAL,
+ .rotate = 2,
+ .flip = 1
+ }};
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ for (i = 0; i < G_N_ELEMENTS (buttons); i++)
+ {
+ const ButtonInfo *info = &buttons[i];
+ GtkWidget *button;
+ GtkWidget *image;
+
+ button = gtk_button_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_set_tooltip_text (button, info->tooltip);
+ gtk_widget_set_can_focus (button, FALSE);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_show (button);
+
+ image = gtk_image_new_from_icon_name (info->icon_name,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ g_object_set_data (G_OBJECT (button),
+ "convolution-matrix-rotate",
+ GINT_TO_POINTER (info->rotate));
+ g_object_set_data (G_OBJECT (button),
+ "convolution-matrix-flip",
+ GINT_TO_POINTER (info->flip));
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (convolution_matrix_rotate_flip),
+ config);
+ }
+ }
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ /* divisor / offset spin scales */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ scale = gimp_prop_widget_new (config, "divisor",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "offset",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
+ gtk_widget_show (scale);
+
+ /* rest of the properties */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ vbox2 = _gimp_prop_gui_new_generic (config,
+ param_specs + 27, 4,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
+ gtk_widget_show (vbox2);
+
+ vbox2 = _gimp_prop_gui_new_generic (config,
+ param_specs + 31,
+ n_param_specs - 31,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, TRUE, TRUE, 0);
+ gtk_widget_show (vbox2);
+
+ return main_vbox;
+}
diff --git a/app/propgui/gimppropgui-convolution-matrix.h b/app/propgui/gimppropgui-convolution-matrix.h
new file mode 100644
index 0000000..21278c5
--- /dev/null
+++ b/app/propgui/gimppropgui-convolution-matrix.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-convolution-matrix.h
+ *
+ * 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_PROP_GUI_CONVOLUTION_MATRIX_H__
+#define __GIMP_PROP_GUI_CONVOLUTION_MATRIX_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_convolution_matrix (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_CONVOLUTION_MATRIX_H__ */
diff --git a/app/propgui/gimppropgui-diffraction-patterns.c b/app/propgui/gimppropgui-diffraction-patterns.c
new file mode 100644
index 0000000..ce4dc87
--- /dev/null
+++ b/app/propgui/gimppropgui-diffraction-patterns.c
@@ -0,0 +1,105 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-diffraction-patterns.c
+ * Copyright (C) 2002-2014 Michael Natterer <mitch@gimp.org>
+ * 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 "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-diffraction-patterns.h"
+#include "gimppropgui-generic.h"
+
+#include "gimp-intl.h"
+
+
+GtkWidget *
+_gimp_prop_gui_new_diffraction_patterns (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *notebook;
+ GtkWidget *vbox;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ notebook = gtk_notebook_new ();
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs + 0, 3,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new (_("Frequencies")));
+ gtk_widget_show (vbox);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs + 3, 3,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new (_("Contours")));
+ gtk_widget_show (vbox);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs + 6, 3,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new (_("Sharp Edges")));
+ gtk_widget_show (vbox);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs + 9, 3,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new (_("Other Options")));
+ gtk_widget_show (vbox);
+
+ return notebook;
+}
diff --git a/app/propgui/gimppropgui-diffraction-patterns.h b/app/propgui/gimppropgui-diffraction-patterns.h
new file mode 100644
index 0000000..dd6bbc5
--- /dev/null
+++ b/app/propgui/gimppropgui-diffraction-patterns.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-diffraction-patterns.h
+ *
+ * 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_PROP_GUI_DIFFRACTION_PATTERNS_H__
+#define __GIMP_PROP_GUI_DIFFRACTION_PATTERNS_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_diffraction_patterns (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_DIFFRACTION_PATTERNS_H__ */
diff --git a/app/propgui/gimppropgui-eval.c b/app/propgui/gimppropgui-eval.c
new file mode 100644
index 0000000..719e500
--- /dev/null
+++ b/app/propgui/gimppropgui-eval.c
@@ -0,0 +1,1039 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-eval.c
+ * Copyright (C) 2017 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Less General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* This is a simple interpreter for the GUM language (the GEGL UI Meta
+ * language), used in certain property keys of GEGL operations. What follows
+ * is a hand-wavy summary of the syntax and semantics (no BNF for you!)
+ *
+ * There are currently two types of expressions:
+ *
+ * Boolean expressions
+ * -------------------
+ *
+ * There are three types of simple boolean expressions:
+ *
+ * - Literal: Either `0` or `1`, evaluating to FALSE and TRUE, respectively.
+ *
+ * - Reference: Has the form `$key` or `$property.key`. Evaluates to the
+ * value of key `key`, which should itself be a boolean expression. In
+ * the first form, `key` refers to a key of the same property to which the
+ * currently-evaluated key belongs. In the second form, `key` refers to a
+ * key of `property`.
+ *
+ * - Dependency: Dependencies begin with the name of a property, on which
+ * the result depends. Currently supported property types are:
+ *
+ * - Boolean: The expression consists of the property name alone, and
+ * its value is the value of the property.
+ *
+ * - Enum: The property name shall be followed by a brace-enclosed,
+ * comma-separated list of enum values, given as nicks. The expression
+ * evaluates to TRUE iff the property matches any of the values.
+ *
+ * Complex boolean expressions can be formed using `!` (negation), `&`
+ * (conjunction), `|` (disjunction), and parentheses (grouping), following the
+ * usual precedence rules.
+ *
+ * String expressions
+ * ------------------
+ *
+ * There are three types of simple string expressions:
+ *
+ * - Literal: A string literal, surrounded by single quotes (`'`). Special
+ * characters (in particular, single quotes) can be escaped using a
+ * backslash (`\`).
+ *
+ * - Reference: Same as a boolean reference, but should refer to a key
+ * containing a string expression.
+ *
+ * - Deferred literal: Names a key, in the same fashion as a reference, but
+ * without the leading `$`. The value of this key is taken literally as
+ * the value of the expression. Deferred literals should usually be
+ * favored over inline string literals, because they can be translated
+ * independently of the expression.
+ *
+ * Currently, the only complex string expression is string selection: It has
+ * the form of a bracket-enclosed, comma-separated list of expressions of the
+ * form `<condition> : <value>`, where `<condition>` is a boolean expression,
+ * and `<value>` is a string expression. The result of the expression is the
+ * associated value of the first condition that evaluates to TRUE. If no
+ * condition is met, the result is NULL.
+ *
+ *
+ * Whitespace separating subexpressions is insignificant.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gegl-paramspecs.h>
+#include <gtk/gtk.h>
+
+#include "propgui-types.h"
+
+#include "gimppropgui-eval.h"
+
+
+typedef enum
+{
+ GIMP_PROP_EVAL_FAILED /* generic error condition */
+} GimpPropEvalErrorCode;
+
+
+static gboolean gimp_prop_eval_boolean_impl (GObject *config,
+ GParamSpec *pspec,
+ const gchar *key,
+ gint default_value,
+ GError **error,
+ gint depth);
+static gboolean gimp_prop_eval_boolean_or (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth);
+static gboolean gimp_prop_eval_boolean_and (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth);
+static gboolean gimp_prop_eval_boolean_not (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth);
+static gboolean gimp_prop_eval_boolean_group (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth);
+static gboolean gimp_prop_eval_boolean_simple (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth);
+
+static gchar * gimp_prop_eval_string_impl (GObject *config,
+ GParamSpec *pspec,
+ const gchar *key,
+ const gchar *default_value,
+ GError **error,
+ gint depth);
+static gchar * gimp_prop_eval_string_selection (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth);
+static gchar * gimp_prop_eval_string_simple (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth);
+
+static gboolean gimp_prop_eval_parse_reference (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ GParamSpec **ref_pspec,
+ gchar **ref_key);
+
+static gboolean gimp_prop_eval_depth_test (gint depth,
+ GError **error);
+
+static gchar * gimp_prop_eval_read_token (const gchar **expr,
+ gchar **t,
+ GError **error);
+static gboolean gimp_prop_eval_is_name (const gchar *token);
+
+#define GIMP_PROP_EVAL_ERROR (gimp_prop_eval_error_quark ())
+
+static GQuark gimp_prop_eval_error_quark (void);
+
+
+/* public functions */
+
+gboolean
+gimp_prop_eval_boolean (GObject *config,
+ GParamSpec *pspec,
+ const gchar *key,
+ gboolean default_value)
+{
+ GError *error = NULL;
+ gboolean result;
+
+ result = gimp_prop_eval_boolean_impl (config, pspec,
+ key, default_value, &error, 0);
+
+ if (error)
+ {
+ g_warning ("in object of type '%s': %s",
+ G_OBJECT_TYPE_NAME (config),
+ error->message);
+
+ g_error_free (error);
+
+ return default_value;
+ }
+
+ return result;
+}
+
+gchar *
+gimp_prop_eval_string (GObject *config,
+ GParamSpec *pspec,
+ const gchar *key,
+ const gchar *default_value)
+{
+ GError *error = NULL;
+ gchar *result;
+
+ result = gimp_prop_eval_string_impl (config, pspec,
+ key, default_value, &error, 0);
+
+ if (error)
+ {
+ g_warning ("in object of type '%s': %s",
+ G_OBJECT_TYPE_NAME (config),
+ error->message);
+
+ g_error_free (error);
+
+ return g_strdup (default_value);
+ }
+
+ return result;
+}
+
+
+/* private functions */
+
+static gboolean
+gimp_prop_eval_boolean_impl (GObject *config,
+ GParamSpec *pspec,
+ const gchar *key,
+ gint default_value,
+ GError **error,
+ gint depth)
+{
+ const gchar *expr;
+ gchar *t = NULL;
+ gboolean result = FALSE;
+
+ if (! gimp_prop_eval_depth_test (depth, error))
+ return FALSE;
+
+ expr = gegl_param_spec_get_property_key (pspec, key);
+
+ if (! expr)
+ {
+ /* we use `default_value < 0` to specify that the key must exist */
+ if (default_value < 0)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "key '%s' of property '%s' not found",
+ key,
+ g_param_spec_get_name (pspec));
+
+ return FALSE;
+ }
+
+ return default_value;
+ }
+
+ gimp_prop_eval_read_token (&expr, &t, error);
+
+ if (! *error)
+ {
+ result = gimp_prop_eval_boolean_or (config, pspec,
+ &expr, &t, error, depth);
+ }
+
+ /* check for trailing tokens at the end of the expression */
+ if (! *error && t)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid expression");
+ }
+
+ g_free (t);
+
+ if (*error)
+ {
+ g_prefix_error (error,
+ "in key '%s' of property '%s': ",
+ key,
+ g_param_spec_get_name (pspec));
+
+ return FALSE;
+ }
+
+ return result;
+}
+
+static gboolean
+gimp_prop_eval_boolean_or (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth)
+{
+ gboolean result;
+
+ if (! gimp_prop_eval_depth_test (depth, error))
+ return FALSE;
+
+ result = gimp_prop_eval_boolean_and (config, pspec,
+ expr, t, error, depth);
+
+ while (! *error && ! g_strcmp0 (*t, "|"))
+ {
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ return FALSE;
+
+ /* keep evaluating even if `result` is TRUE, because we still need to
+ * parse the rest of the subexpression.
+ */
+ result |= gimp_prop_eval_boolean_and (config, pspec,
+ expr, t, error, depth);
+ }
+
+ return result;
+}
+
+static gboolean
+gimp_prop_eval_boolean_and (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth)
+{
+ gboolean result;
+
+ if (! gimp_prop_eval_depth_test (depth, error))
+ return FALSE;
+
+ result = gimp_prop_eval_boolean_not (config, pspec,
+ expr, t, error, depth);
+
+ while (! *error && ! g_strcmp0 (*t, "&"))
+ {
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ return FALSE;
+
+ /* keep evaluating even if `result` is FALSE, because we still need to
+ * parse the rest of the subexpression.
+ */
+ result &= gimp_prop_eval_boolean_not (config, pspec,
+ expr, t, error, depth);
+ }
+
+ return result;
+}
+
+static gboolean
+gimp_prop_eval_boolean_not (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth)
+{
+ if (! gimp_prop_eval_depth_test (depth, error))
+ return FALSE;
+
+ if (! g_strcmp0 (*t, "!"))
+ {
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ return FALSE;
+
+ return ! gimp_prop_eval_boolean_not (config, pspec,
+ expr, t, error, depth + 1);
+ }
+
+ return gimp_prop_eval_boolean_group (config, pspec,
+ expr, t, error, depth);
+}
+
+static gboolean
+gimp_prop_eval_boolean_group (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth)
+{
+ if (! gimp_prop_eval_depth_test (depth, error))
+ return FALSE;
+
+ if (! g_strcmp0 (*t, "("))
+ {
+ gboolean result;
+
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ return FALSE;
+
+ result = gimp_prop_eval_boolean_or (config, pspec,
+ expr, t, error, depth + 1);
+
+ if (*error)
+ return FALSE;
+
+ if (g_strcmp0 (*t, ")"))
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "unterminated group");
+
+ return FALSE;
+ }
+
+ gimp_prop_eval_read_token (expr, t, error);
+
+ return result;
+ }
+
+ return gimp_prop_eval_boolean_simple (config, pspec,
+ expr, t, error, depth);
+}
+
+static gboolean
+gimp_prop_eval_boolean_simple (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth)
+{
+ gboolean result;
+
+ if (! gimp_prop_eval_depth_test (depth, error))
+ return FALSE;
+
+ if (! *t)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid expression");
+
+ return FALSE;
+ }
+
+ /* literal */
+ if (! strcmp (*t, "0"))
+ {
+ result = FALSE;
+
+ gimp_prop_eval_read_token (expr, t, error);
+ }
+ else if (! strcmp (*t, "1"))
+ {
+ result = TRUE;
+
+ gimp_prop_eval_read_token (expr, t, error);
+ }
+ /* reference */
+ else if (! strcmp (*t, "$"))
+ {
+ gchar *key;
+
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ return FALSE;
+
+ if (! gimp_prop_eval_parse_reference (config, pspec,
+ expr, t, error, &pspec, &key))
+ return FALSE;
+
+ result = gimp_prop_eval_boolean_impl (config, pspec,
+ key, -1, error, depth + 1);
+
+ g_free (key);
+ }
+ /* dependency */
+ else if (gimp_prop_eval_is_name (*t))
+ {
+ const gchar *property_name;
+ GParamSpec *pspec;
+ GType type;
+
+ property_name = *t;
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
+ property_name);
+
+ if (! pspec)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "property '%s' not found",
+ property_name);
+
+ return TRUE;
+ }
+
+ property_name = g_param_spec_get_name (pspec);
+ type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+
+ if (g_type_is_a (type, G_TYPE_BOOLEAN))
+ {
+ g_object_get (config, property_name, &result, NULL);
+ }
+ else if (g_type_is_a (type, G_TYPE_ENUM))
+ {
+ GEnumClass *enum_class;
+ gint value;
+
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ return FALSE;
+
+ if (g_strcmp0 (*t , "{"))
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "missing enum value set "
+ "for property '%s'",
+ property_name);
+
+ return FALSE;
+ }
+
+ enum_class = g_type_class_peek (type);
+
+ g_object_get (config, property_name, &value, NULL);
+
+ result = FALSE;
+
+ while (gimp_prop_eval_read_token (expr, t, error) &&
+ gimp_prop_eval_is_name (*t))
+ {
+ const gchar *nick;
+ GEnumValue *enum_value;
+
+ nick = *t;
+ enum_value = g_enum_get_value_by_nick (enum_class, nick);
+
+ if (! enum_value)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid enum value '%s' "
+ "for property '%s'",
+ nick,
+ property_name);
+
+ return FALSE;
+ }
+
+ if (value == enum_value->value)
+ result = TRUE;
+
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ return FALSE;
+
+ if (! g_strcmp0 (*t, ","))
+ {
+ continue;
+ }
+ else if (! g_strcmp0 (*t, "}"))
+ {
+ break;
+ }
+ else
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid enum value set "
+ "for property '%s'",
+ property_name);
+
+ return FALSE;
+ }
+ }
+
+ if (*error)
+ return FALSE;
+
+ if (g_strcmp0 (*t, "}"))
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "unterminated enum value set "
+ "for property '%s'",
+ property_name);
+
+ return FALSE;
+ }
+ }
+ else
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid type "
+ "for property '%s'",
+ property_name);
+
+ return FALSE;
+ }
+
+ gimp_prop_eval_read_token (expr, t, error);
+ }
+ else
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid expression");
+
+ return FALSE;
+ }
+
+ return result;
+}
+
+static gchar *
+gimp_prop_eval_string_impl (GObject *config,
+ GParamSpec *pspec,
+ const gchar *key,
+ const gchar *default_value,
+ GError **error,
+ gint depth)
+{
+ const gchar *expr;
+ gchar *t = NULL;
+ gchar *result = NULL;
+
+ if (! gimp_prop_eval_depth_test (depth, error))
+ return NULL;
+
+ expr = gegl_param_spec_get_property_key (pspec, key);
+
+ if (! expr)
+ return g_strdup (default_value);
+
+ gimp_prop_eval_read_token (&expr, &t, error);
+
+ if (! *error)
+ {
+ result = gimp_prop_eval_string_selection (config, pspec,
+ &expr, &t, error, depth);
+ }
+
+ /* check for trailing tokens at the end of the expression */
+ if (! *error && t)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid expression");
+
+ g_free (result);
+ }
+
+ g_free (t);
+
+ if (*error)
+ {
+ g_prefix_error (error,
+ "in key '%s' of property '%s': ",
+ key,
+ g_param_spec_get_name (pspec));
+
+ return NULL;
+ }
+
+ if (result)
+ return result;
+ else
+ return g_strdup (default_value);
+}
+
+static gchar *
+gimp_prop_eval_string_selection (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth)
+{
+ if (! gimp_prop_eval_depth_test (depth, error) || ! t)
+ return NULL;
+
+ if (! g_strcmp0 (*t, "["))
+ {
+ gboolean match = FALSE;
+ gchar *result = NULL;
+
+ if (! g_strcmp0 (gimp_prop_eval_read_token (expr, t, error), "]"))
+ return NULL;
+
+ while (! *error)
+ {
+ gboolean cond;
+ gchar *value;
+
+ cond = gimp_prop_eval_boolean_or (config, pspec,
+ expr, t, error, depth + 1);
+
+ if (*error)
+ break;
+
+ if (g_strcmp0 (*t, ":"))
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "missing string selection value");
+
+ break;
+ }
+
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ break;
+
+ value = gimp_prop_eval_string_selection (config, pspec,
+ expr, t, error, depth + 1);
+
+ if (*error)
+ break;
+
+ if (! match && cond)
+ {
+ match = TRUE;
+ result = value;
+ }
+
+ if (! g_strcmp0 (*t, ","))
+ {
+ gimp_prop_eval_read_token (expr, t, error);
+
+ continue;
+ }
+ else if (! g_strcmp0 (*t, "]"))
+ {
+ gimp_prop_eval_read_token (expr, t, error);
+
+ break;
+ }
+ else
+ {
+ if (*t)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid string selection");
+ }
+ else
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "unterminated string selection");
+ }
+
+ break;
+ }
+ }
+
+ if (*error)
+ {
+ g_free (result);
+
+ return NULL;
+ }
+
+ return result;
+ }
+
+ return gimp_prop_eval_string_simple (config,
+ pspec, expr, t, error, depth);
+}
+
+static gchar *
+gimp_prop_eval_string_simple (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ gint depth)
+{
+ gchar *result = NULL;
+
+ if (! gimp_prop_eval_depth_test (depth, error))
+ return NULL;
+
+ /* literal */
+ if (*t && **t == '\'')
+ {
+ gchar *escaped;
+
+ escaped = g_strndup (*t + 1, strlen (*t + 1) - 1);
+
+ result = g_strcompress (escaped);
+
+ g_free (escaped);
+
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ {
+ g_free (result);
+
+ return NULL;
+ }
+ }
+ /* reference */
+ else if (! g_strcmp0 (*t, "$"))
+ {
+ gchar *key;
+
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ return NULL;
+
+ if (! gimp_prop_eval_parse_reference (config, pspec,
+ expr, t, error, &pspec, &key))
+ return NULL;
+
+ result = gimp_prop_eval_string_impl (config, pspec,
+ key, NULL, error, depth + 1);
+
+ g_free (key);
+ }
+ /* deferred literal */
+ else if (gimp_prop_eval_is_name (*t))
+ {
+ GParamSpec *str_pspec;
+ gchar *str_key;
+ const gchar *str;
+
+ if (! gimp_prop_eval_parse_reference (config, pspec,
+ expr, t, error,
+ &str_pspec, &str_key))
+ return NULL;
+
+ str = gegl_param_spec_get_property_key (str_pspec, str_key);
+
+ if (! str)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "key '%s' of property '%s' not found",
+ str_key,
+ g_param_spec_get_name (str_pspec));
+
+ g_free (str_key);
+
+ return NULL;
+ }
+
+ g_free (str_key);
+
+ result = g_strdup (str);
+ }
+ else
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid expression");
+
+ return NULL;
+ }
+
+ return result;
+}
+
+static gboolean
+gimp_prop_eval_parse_reference (GObject *config,
+ GParamSpec *pspec,
+ const gchar **expr,
+ gchar **t,
+ GError **error,
+ GParamSpec **ref_pspec,
+ gchar **ref_key)
+{
+ if (! gimp_prop_eval_is_name (*t))
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid reference");
+
+ return FALSE;
+ }
+
+ *ref_pspec = pspec;
+ *ref_key = g_strdup (*t);
+
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ {
+ g_free (*ref_key);
+
+ return FALSE;
+ }
+
+ if (! g_strcmp0 (*t, "."))
+ {
+ gchar *property_name;
+
+ property_name = *ref_key;
+
+ if (! gimp_prop_eval_read_token (expr, t, error) ||
+ ! gimp_prop_eval_is_name (*t))
+ {
+ if (! *error)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "invalid reference");
+ }
+
+ g_free (property_name);
+
+ return FALSE;
+ }
+
+ *ref_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
+ property_name);
+
+ if (! *ref_pspec)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "property '%s' not found",
+ property_name);
+
+ g_free (property_name);
+
+ return FALSE;
+ }
+
+ g_free (property_name);
+
+ *ref_key = g_strdup (*t);
+
+ gimp_prop_eval_read_token (expr, t, error);
+
+ if (*error)
+ {
+ g_free (*ref_key);
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_prop_eval_depth_test (gint depth,
+ GError **error)
+{
+ /* make sure we don't recurse too deep. in particular, guard against
+ * circular references.
+ */
+ if (depth == 100)
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "maximal nesting level exceeded");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gchar *
+gimp_prop_eval_read_token (const gchar **expr,
+ gchar **t,
+ GError **error)
+{
+ const gchar *token;
+
+ g_free (*t);
+ *t = NULL;
+
+ /* skip whitespace */
+ while (g_ascii_isspace (**expr))
+ ++*expr;
+
+ token = *expr;
+
+ if (*token == '\0')
+ return NULL;
+
+ /* name */
+ if (gimp_prop_eval_is_name (token))
+ {
+ do { ++*expr; } while (g_ascii_isalnum (**expr) ||
+ **expr == '_' ||
+ **expr == '-');
+ }
+ /* string literal */
+ else if (token[0] == '\'')
+ {
+ for (++*expr; **expr != '\0' && **expr != '\''; ++*expr)
+ {
+ if (**expr == '\\')
+ {
+ ++*expr;
+
+ if (**expr == '\0')
+ break;
+ }
+ }
+
+ if (**expr == '\0')
+ {
+ g_set_error (error, GIMP_PROP_EVAL_ERROR, GIMP_PROP_EVAL_FAILED,
+ "unterminated string literal");
+
+ return NULL;
+ }
+
+ ++*expr;
+ }
+ /* punctuation or boolean literal */
+ else
+ {
+ ++*expr;
+ }
+
+ *t = g_strndup (token, *expr - token);
+
+ return *t;
+}
+
+static gboolean
+gimp_prop_eval_is_name (const gchar *token)
+{
+ return token && (g_ascii_isalpha (*token) || *token == '_');
+}
+
+static GQuark
+gimp_prop_eval_error_quark (void)
+{
+ return g_quark_from_static_string ("gimp-prop-eval-error-quark");
+}
diff --git a/app/propgui/gimppropgui-eval.h b/app/propgui/gimppropgui-eval.h
new file mode 100644
index 0000000..e2b8708
--- /dev/null
+++ b/app/propgui/gimppropgui-eval.h
@@ -0,0 +1,36 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui.h
+ * Copyright (C) 2017 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PROP_GUI_EVAL_H__
+#define __GIMP_PROP_GUI_EVAL_H__
+
+
+gboolean gimp_prop_eval_boolean (GObject *config,
+ GParamSpec *pspec,
+ const gchar *key,
+ gboolean default_value);
+
+gchar * gimp_prop_eval_string (GObject *config,
+ GParamSpec *pspec,
+ const gchar *key,
+ const gchar *default_value);
+
+
+#endif /* __GIMP_PROP_GUI_EVAL_H__ */
diff --git a/app/propgui/gimppropgui-focus-blur.c b/app/propgui/gimppropgui-focus-blur.c
new file mode 100644
index 0000000..ecee097
--- /dev/null
+++ b/app/propgui/gimppropgui-focus-blur.c
@@ -0,0 +1,246 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-focus-blur.c
+ * Copyright (C) 2020 Ell
+ *
+ * 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-generic.h"
+#include "gimppropgui-focus-blur.h"
+
+#include "gimp-intl.h"
+
+
+static gint
+find_param (GParamSpec **param_specs,
+ guint n_param_specs,
+ const gchar *name)
+{
+ gint i;
+
+ for (i = 0; i < n_param_specs; i++)
+ {
+ if (! strcmp (param_specs[i]->name, name))
+ break;
+ }
+
+ return i;
+}
+
+static void
+focus_callback (GObject *config,
+ GeglRectangle *area,
+ GimpLimitType type,
+ gdouble x,
+ gdouble y,
+ gdouble radius,
+ gdouble aspect_ratio,
+ gdouble angle,
+ gdouble inner_limit,
+ gdouble midpoint)
+{
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ g_object_set (config,
+ "shape", type,
+ "x", x / area->width,
+ "y", y / area->height,
+ "radius", 2.0 * radius / area->width,
+ "focus", inner_limit,
+ "midpoint", midpoint,
+ "aspect-ratio", aspect_ratio,
+ "rotation", fmod (
+ fmod (angle * 180.0 / G_PI + 180.0, 360.0) +
+ 360.0,
+ 360.0) - 180.0,
+ NULL);
+}
+
+static void
+config_notify (GObject *config,
+ const GParamSpec *pspec,
+ gpointer set_data)
+{
+ GimpControllerFocusCallback set_func;
+ GeglRectangle *area;
+ GimpLimitType shape;
+ gdouble radius;
+ gdouble focus;
+ gdouble midpoint;
+ gdouble x, y;
+ gdouble aspect_ratio;
+ gdouble rotation;
+
+ set_func = g_object_get_data (G_OBJECT (config), "set-func");
+ area = g_object_get_data (G_OBJECT (config), "area");
+
+ g_object_get (config,
+ "shape", &shape,
+ "radius", &radius,
+ "focus", &focus,
+ "midpoint", &midpoint,
+ "x", &x,
+ "y", &y,
+ "aspect-ratio", &aspect_ratio,
+ "rotation", &rotation,
+ NULL);
+
+ set_func (set_data, area,
+ shape,
+ x * area->width,
+ y * area->height,
+ radius * area->width / 2.0,
+ aspect_ratio,
+ rotation / 180.0 * G_PI,
+ focus,
+ midpoint);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_focus_blur (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *vbox;
+ gint first_geometry_param;
+ gint last_geometry_param;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ first_geometry_param = find_param (param_specs, n_param_specs,
+ "shape") + 1;
+ last_geometry_param = find_param (param_specs, n_param_specs,
+ "high-quality");
+
+ if (last_geometry_param <= first_geometry_param)
+ {
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs, n_param_specs,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ }
+ else
+ {
+ GtkWidget *widget;
+ GtkWidget *expander;
+ GtkWidget *frame;
+ const gchar *label;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+
+ widget = gimp_prop_widget_new (config,
+ "shape",
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator,
+ &label);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ widget = _gimp_prop_gui_new_generic (config,
+ param_specs,
+ first_geometry_param - 1,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ widget = _gimp_prop_gui_new_generic (config,
+ param_specs + last_geometry_param,
+ n_param_specs - last_geometry_param,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ expander = gtk_expander_new (_("Geometry Options"));
+ gtk_box_pack_start (GTK_BOX (vbox), expander, FALSE, FALSE, 0);
+ gtk_widget_show (expander);
+
+ frame = gimp_frame_new (NULL);
+ gtk_container_add (GTK_CONTAINER (expander), frame);
+ gtk_widget_show (frame);
+
+ widget = _gimp_prop_gui_new_generic (config,
+ param_specs + first_geometry_param,
+ last_geometry_param -
+ first_geometry_param,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_container_add (GTK_CONTAINER (frame), widget);
+ gtk_widget_show (widget);
+ }
+
+ if (create_controller_func)
+ {
+ GCallback set_func;
+ gpointer set_data;
+
+ set_func = create_controller_func (creator,
+ GIMP_CONTROLLER_TYPE_FOCUS,
+ _("Focus Blur: "),
+ (GCallback) focus_callback,
+ config,
+ &set_data);
+
+ g_object_set_data (G_OBJECT (config), "set-func", set_func);
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ config_notify (config, NULL, set_data);
+
+ g_signal_connect (config, "notify",
+ G_CALLBACK (config_notify),
+ set_data);
+ }
+
+ return vbox;
+}
+
diff --git a/app/propgui/gimppropgui-focus-blur.h b/app/propgui/gimppropgui-focus-blur.h
new file mode 100644
index 0000000..1c0c0c7
--- /dev/null
+++ b/app/propgui/gimppropgui-focus-blur.h
@@ -0,0 +1,36 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-focus-blur.h
+ * Copyright (C) 2020 Ell
+ *
+ * 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_PROP_GUI_FOCUS_BLUR_H__
+#define __GIMP_PROP_GUI_FOCUS_BLUR_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_focus_blur (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_FOCUS_BLUR_H__ */
diff --git a/app/propgui/gimppropgui-generic.c b/app/propgui/gimppropgui-generic.c
new file mode 100644
index 0000000..a7c7e89
--- /dev/null
+++ b/app/propgui/gimppropgui-generic.c
@@ -0,0 +1,377 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-generic.c
+ * Copyright (C) 2002-2014 Michael Natterer <mitch@gimp.org>
+ * 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 <string.h>
+#include <stdlib.h>
+
+#include <gegl.h>
+#include <gegl-paramspecs.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "gegl/gimp-gegl-utils.h"
+
+#include "core/gimpcontext.h"
+
+#include "widgets/gimppropwidgets.h"
+#include "widgets/gimpspinscale.h"
+#include "widgets/gimpwidgets-utils.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-generic.h"
+
+#include "gimp-intl.h"
+
+
+#define HAS_KEY(p,k,v) gimp_gegl_param_spec_has_key (p, k, v)
+
+
+static void gimp_prop_gui_chain_toggled (GimpChainButton *chain,
+ GtkAdjustment *x_adj);
+
+
+/* public functions */
+
+GtkWidget *
+_gimp_prop_gui_new_generic (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *main_vbox;
+ GtkSizeGroup *label_group;
+ GList *chains = NULL;
+ gint i;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+
+ label_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ for (i = 0; i < n_param_specs; i++)
+ {
+ GParamSpec *pspec = param_specs[i];
+ GParamSpec *next_pspec = NULL;
+
+ if (i < n_param_specs - 1)
+ next_pspec = param_specs[i + 1];
+
+ if (next_pspec &&
+ HAS_KEY (pspec, "axis", "x") &&
+ HAS_KEY (next_pspec, "axis", "y"))
+ {
+ GtkWidget *widget_x;
+ GtkWidget *widget_y;
+ const gchar *label_x;
+ const gchar *label_y;
+ GtkAdjustment *adj_x;
+ GtkAdjustment *adj_y;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *chain;
+
+ i++;
+
+ widget_x = gimp_prop_widget_new_from_pspec (config, pspec,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator,
+ &label_x);
+ widget_y = gimp_prop_widget_new_from_pspec (config, next_pspec,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator,
+ &label_y);
+
+ adj_x = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget_x));
+ adj_y = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget_y));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ gimp_prop_gui_bind_container (widget_x, hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ gtk_box_pack_start (GTK_BOX (vbox), widget_x, FALSE, FALSE, 0);
+ gtk_widget_show (widget_x);
+
+ gtk_box_pack_start (GTK_BOX (vbox), widget_y, FALSE, FALSE, 0);
+ gtk_widget_show (widget_y);
+
+ chain = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
+ gtk_box_pack_end (GTK_BOX (hbox), chain, FALSE, FALSE, 0);
+ gtk_widget_show (chain);
+
+ if (! HAS_KEY (pspec, "unit", "pixel-coordinate") &&
+ ! HAS_KEY (pspec, "unit", "relative-coordinate") &&
+ gtk_adjustment_get_value (adj_x) ==
+ gtk_adjustment_get_value (adj_y))
+ {
+ GBinding *binding;
+
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain), TRUE);
+
+ binding = g_object_bind_property (adj_x, "value",
+ adj_y, "value",
+ G_BINDING_BIDIRECTIONAL);
+
+ g_object_set_data (G_OBJECT (chain), "binding", binding);
+ }
+
+ g_object_set_data_full (G_OBJECT (chain), "x-property",
+ g_strdup (pspec->name), g_free);
+ g_object_set_data_full (G_OBJECT (chain), "y-property",
+ g_strdup (next_pspec->name), g_free);
+
+ chains = g_list_prepend (chains, chain);
+
+ g_signal_connect (chain, "toggled",
+ G_CALLBACK (gimp_prop_gui_chain_toggled),
+ adj_x);
+
+ g_object_set_data (G_OBJECT (adj_x), "y-adjustment", adj_y);
+
+ if (create_picker_func &&
+ (HAS_KEY (pspec, "unit", "pixel-coordinate") ||
+ HAS_KEY (pspec, "unit", "relative-coordinate")))
+ {
+ GtkWidget *button;
+ gchar *pspec_name;
+
+ pspec_name = g_strconcat (pspec->name, ":",
+ next_pspec->name, NULL);
+
+ button = create_picker_func (creator,
+ pspec_name,
+ GIMP_ICON_CURSOR,
+ _("Pick coordinates from the image"),
+ /* pick_abyss = */ TRUE,
+ NULL, NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_object_weak_ref (G_OBJECT (button),
+ (GWeakNotify) g_free, pspec_name);
+ }
+ }
+ else if (next_pspec &&
+ HAS_KEY (pspec, "role", "range-start") &&
+ HAS_KEY (next_pspec, "role", "range-end") &&
+ HAS_KEY (pspec, "unit", "luminance"))
+ {
+ GtkWidget *vbox;
+ GtkWidget *spin_scale;
+ GtkWidget *label;
+ GtkWidget *frame;
+ GtkWidget *range;
+ const gchar *label_str;
+ const gchar *range_label_str;
+ gdouble step_increment;
+ gdouble page_increment;
+ gdouble ui_lower;
+ gdouble ui_upper;
+
+ i++;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+
+ spin_scale = gimp_prop_widget_new_from_pspec (
+ config, pspec,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator,
+ &label_str);
+ gtk_widget_show (spin_scale);
+
+ g_object_set_data_full (G_OBJECT (vbox),
+ "gimp-underlying-widget",
+ g_object_ref_sink (spin_scale),
+ g_object_unref);
+
+ range_label_str = gegl_param_spec_get_property_key (pspec,
+ "range-label");
+
+ if (range_label_str)
+ label_str = range_label_str;
+
+ gtk_spin_button_get_increments (GTK_SPIN_BUTTON (spin_scale),
+ &step_increment, &page_increment);
+
+ gimp_spin_scale_get_scale_limits (GIMP_SPIN_SCALE (spin_scale),
+ &ui_lower, &ui_upper);
+
+ label = gtk_label_new_with_mnemonic (label_str);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ if (! range_label_str)
+ {
+ g_object_bind_property (spin_scale, "label",
+ label, "label",
+ G_BINDING_SYNC_CREATE);
+ }
+
+ frame = gimp_frame_new (NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ range = gimp_prop_range_new (config,
+ pspec->name, next_pspec->name,
+ step_increment, page_increment,
+ gtk_spin_button_get_digits (
+ GTK_SPIN_BUTTON (spin_scale)),
+ ! HAS_KEY (pspec,
+ "range-sorted", "false"));
+ gimp_prop_range_set_ui_limits (range, ui_lower, ui_upper);
+ gtk_container_add (GTK_CONTAINER (frame), range);
+ gtk_widget_show (range);
+
+ gimp_prop_gui_bind_container (spin_scale, vbox);
+ gimp_prop_gui_bind_tooltip (spin_scale, vbox);
+ }
+ else
+ {
+ GtkWidget *widget;
+ const gchar *label;
+ gboolean expand = FALSE;
+
+ widget = gimp_prop_widget_new_from_pspec (config, pspec,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator,
+ &label);
+
+ if (GTK_IS_SCROLLED_WINDOW (widget))
+ expand = TRUE;
+
+ if (widget && label)
+ {
+ GtkWidget *l;
+
+ l = gtk_label_new_with_mnemonic (label);
+ gtk_label_set_xalign (GTK_LABEL (l), 0.0);
+ gtk_widget_show (l);
+
+ gimp_prop_gui_bind_label (widget, l);
+
+ if (GTK_IS_SCROLLED_WINDOW (widget))
+ {
+ GtkWidget *frame;
+
+ /* don't set as frame title, it should not be bold */
+ gtk_box_pack_start (GTK_BOX (main_vbox), l, FALSE, FALSE, 0);
+
+ frame = gimp_frame_new (NULL);
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ gtk_container_add (GTK_CONTAINER (frame), widget);
+ gtk_widget_show (widget);
+
+ gimp_prop_gui_bind_container (widget, frame);
+ }
+ else
+ {
+ GtkWidget *hbox;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox,
+ expand, expand, 0);
+ gtk_widget_show (hbox);
+
+ gtk_size_group_add_widget (label_group, l);
+ gtk_box_pack_start (GTK_BOX (hbox), l, FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ gimp_prop_gui_bind_container (widget, hbox);
+ }
+ }
+ else if (widget)
+ {
+ gtk_box_pack_start (GTK_BOX (main_vbox), widget,
+ expand, expand, 0);
+ gtk_widget_show (widget);
+ }
+ }
+ }
+
+ g_object_unref (label_group);
+
+ g_object_set_data_full (G_OBJECT (main_vbox), "chains", chains,
+ (GDestroyNotify) g_list_free);
+
+ return main_vbox;
+}
+
+
+/* private functions */
+
+static void
+gimp_prop_gui_chain_toggled (GimpChainButton *chain,
+ GtkAdjustment *x_adj)
+{
+ GBinding *binding;
+ GtkAdjustment *y_adj;
+
+ binding = g_object_get_data (G_OBJECT (chain), "binding");
+ y_adj = g_object_get_data (G_OBJECT (x_adj), "y-adjustment");
+
+ if (gimp_chain_button_get_active (chain))
+ {
+ if (! binding)
+ binding = g_object_bind_property (x_adj, "value",
+ y_adj, "value",
+ G_BINDING_BIDIRECTIONAL);
+ }
+ else
+ {
+ g_clear_object (&binding);
+ }
+ g_object_set_data (G_OBJECT (chain), "binding", binding);
+}
diff --git a/app/propgui/gimppropgui-generic.h b/app/propgui/gimppropgui-generic.h
new file mode 100644
index 0000000..40e5269
--- /dev/null
+++ b/app/propgui/gimppropgui-generic.h
@@ -0,0 +1,36 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-generic.h
+ * Copyright (C) 2002-2014 Michael Natterer <mitch@gimp.org>
+ * 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/>.
+ */
+
+#ifndef __GIMP_PROP_GUI_GENERIC_H__
+#define __GIMP_PROP_GUI_GENERIC_H__
+
+
+GtkWidget * _gimp_prop_gui_new_generic (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_GENERIC_H__ */
diff --git a/app/propgui/gimppropgui-hue-saturation.c b/app/propgui/gimppropgui-hue-saturation.c
new file mode 100644
index 0000000..df96202
--- /dev/null
+++ b/app/propgui/gimppropgui-hue-saturation.c
@@ -0,0 +1,288 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-hue-saturation.c
+ *
+ * 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 "propgui-types.h"
+
+#include "operations/gimphuesaturationconfig.h"
+#include "operations/gimpoperationhuesaturation.h"
+
+#include "core/gimpcontext.h"
+
+#include "widgets/gimppropwidgets.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-hue-saturation.h"
+
+#include "gimp-intl.h"
+
+
+#define COLOR_WIDTH 40
+#define COLOR_HEIGHT 20
+
+
+static void
+hue_saturation_config_notify (GObject *object,
+ const GParamSpec *pspec,
+ GtkWidget *color_area)
+{
+ GimpHueSaturationConfig *config = GIMP_HUE_SATURATION_CONFIG (object);
+ GimpHueRange range;
+ GimpRGB color;
+
+ static const GimpRGB default_colors[7] =
+ {
+ { 0, 0, 0, },
+ { 1.0, 0, 0, },
+ { 1.0, 1.0, 0, },
+ { 0, 1.0, 0, },
+ { 0, 1.0, 1.0, },
+ { 0, 0, 1.0, },
+ { 1.0, 0, 1.0, }
+ };
+
+ range = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (color_area),
+ "hue-range"));
+ color = default_colors[range];
+
+ gimp_operation_hue_saturation_map (config, &color, range, &color);
+
+ gimp_color_area_set_color (GIMP_COLOR_AREA (color_area), &color);
+}
+
+static void
+hue_saturation_range_callback (GtkWidget *widget,
+ GObject *config)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ GimpHueRange range;
+
+ gimp_radio_button_update (widget, &range);
+ g_object_set (config,
+ "range", range,
+ NULL);
+ }
+}
+
+static void
+hue_saturation_range_notify (GObject *object,
+ const GParamSpec *pspec,
+ GtkWidget *range_radio)
+{
+ GimpHueSaturationConfig *config = GIMP_HUE_SATURATION_CONFIG (object);
+
+ gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (range_radio),
+ config->range);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_hue_saturation (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *abox;
+ GtkWidget *table;
+ GtkWidget *scale;
+ GtkWidget *button;
+ GtkWidget *hbox;
+ GtkWidget *range_radio;
+ GSList *group = NULL;
+ gint i;
+
+ const struct
+ {
+ const gchar *label;
+ const gchar *tooltip;
+ gint label_col;
+ gint label_row;
+ gint frame_col;
+ gint frame_row;
+ }
+ hue_range_table[] =
+ {
+ { N_("M_aster"), N_("Adjust all colors"), 2, 3, 0, 0 },
+ { N_("_R"), N_("Red"), 2, 1, 2, 0 },
+ { N_("_Y"), N_("Yellow"), 1, 2, 0, 2 },
+ { N_("_G"), N_("Green"), 1, 4, 0, 4 },
+ { N_("_C"), N_("Cyan"), 2, 5, 2, 6 },
+ { N_("_B"), N_("Blue"), 3, 4, 4, 4 },
+ { N_("_M"), N_("Magenta"), 3, 2, 4, 2 }
+ };
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+
+ frame = gimp_frame_new (_("Select Primary Color to Adjust"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), abox, TRUE, TRUE, 0);
+ gtk_widget_show (abox);
+
+ /* The table containing hue ranges */
+ table = gtk_table_new (7, 5, FALSE);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 4);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 3, 4);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 5, 2);
+ gtk_container_add (GTK_CONTAINER (abox), table);
+
+ /* the radio buttons for hue ranges */
+ for (i = 0; i < G_N_ELEMENTS (hue_range_table); i++)
+ {
+ button = gtk_radio_button_new_with_mnemonic (group,
+ gettext (hue_range_table[i].label));
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ g_object_set_data (G_OBJECT (button), "gimp-item-data",
+ GINT_TO_POINTER (i));
+
+ gimp_help_set_help_data (button,
+ gettext (hue_range_table[i].tooltip),
+ NULL);
+
+ if (i == 0)
+ {
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
+
+ range_radio = button;
+ }
+
+ gtk_table_attach (GTK_TABLE (table), button,
+ hue_range_table[i].label_col,
+ hue_range_table[i].label_col + 1,
+ hue_range_table[i].label_row,
+ hue_range_table[i].label_row + 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+
+ if (i > 0)
+ {
+ GtkWidget *color_area;
+ GimpRGB color = { 0, };
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_table_attach (GTK_TABLE (table), frame,
+ hue_range_table[i].frame_col,
+ hue_range_table[i].frame_col + 1,
+ hue_range_table[i].frame_row,
+ hue_range_table[i].frame_row + 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (frame);
+
+ color_area = gimp_color_area_new (&color, GIMP_COLOR_AREA_FLAT, 0);
+ gtk_widget_set_size_request (color_area, COLOR_WIDTH, COLOR_HEIGHT);
+ gtk_container_add (GTK_CONTAINER (frame), color_area);
+ gtk_widget_show (color_area);
+
+ g_object_set_data (G_OBJECT (color_area), "hue-range",
+ GINT_TO_POINTER (i));
+ g_signal_connect_object (config, "notify",
+ G_CALLBACK (hue_saturation_config_notify),
+ color_area, 0);
+ hue_saturation_config_notify (config, NULL, color_area);
+ }
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (hue_saturation_range_callback),
+ config);
+
+ gtk_widget_show (button);
+ }
+
+ gtk_widget_show (table);
+
+ /* Create the 'Overlap' option slider */
+ scale = gimp_prop_spin_scale_new (config, "overlap",
+ _("_Overlap"), 0.01, 0.1, 0);
+ gimp_prop_widget_set_factor (scale, 100.0, 0.0, 0.0, 1);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ frame = gimp_frame_new (_("Adjust Selected Color"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* Create the hue scale widget */
+ scale = gimp_prop_spin_scale_new (config, "hue",
+ _("_Hue"), 1.0 / 180.0, 15.0 / 180.0, 0);
+ gimp_prop_widget_set_factor (scale, 180.0, 0.0, 0.0, 1);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ /* Create the lightness scale widget */
+ scale = gimp_prop_spin_scale_new (config, "lightness",
+ _("_Lightness"), 0.01, 0.1, 0);
+ gimp_prop_widget_set_factor (scale, 100.0, 0.0, 0.0, 1);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ /* Create the saturation scale widget */
+ scale = gimp_prop_spin_scale_new (config, "saturation",
+ _("_Saturation"), 0.01, 0.1, 0);
+ gimp_prop_widget_set_factor (scale, 100.0, 0.0, 0.0, 1);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ button = gtk_button_new_with_mnemonic (_("R_eset Color"));
+ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_hue_saturation_config_reset_range),
+ config);
+
+ g_signal_connect_object (config, "notify::range",
+ G_CALLBACK (hue_saturation_range_notify),
+ range_radio, 0);
+ hue_saturation_range_notify (config, NULL, range_radio);
+
+ return main_vbox;
+}
diff --git a/app/propgui/gimppropgui-hue-saturation.h b/app/propgui/gimppropgui-hue-saturation.h
new file mode 100644
index 0000000..efeca87
--- /dev/null
+++ b/app/propgui/gimppropgui-hue-saturation.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-hue-saturation.h
+ *
+ * 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_PROP_GUI_HUE_SATURATION_H__
+#define __GIMP_PROP_GUI_HUE_SATURATION_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_hue_saturation (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_HUE_SATURATION_H__ */
diff --git a/app/propgui/gimppropgui-motion-blur-circular.c b/app/propgui/gimppropgui-motion-blur-circular.c
new file mode 100644
index 0000000..a008ef1
--- /dev/null
+++ b/app/propgui/gimppropgui-motion-blur-circular.c
@@ -0,0 +1,151 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-motion-blur-circular.c
+ * Copyright (C) 2019 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui-generic.h"
+#include "gimppropgui-motion-blur-circular.h"
+
+#include "gimp-intl.h"
+
+
+static void
+line_callback (GObject *config,
+ GeglRectangle *area,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+ gdouble x, y;
+ gdouble angle;
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ x = x1 / area->width;
+ y = y1 / area->height;
+ angle = atan2 (-(y2 - y1), x2 - x1);
+
+ angle = angle * 180.0 / G_PI;
+
+ if (angle < 0)
+ angle += 360.0;
+
+ g_object_set (config,
+ "center-x", x,
+ "center-y", y,
+ "angle", angle,
+ NULL);
+}
+
+static void
+config_notify (GObject *config,
+ const GParamSpec *pspec,
+ gpointer set_data)
+{
+ GimpControllerLineCallback set_func;
+ GeglRectangle *area;
+ gdouble x, y;
+ gdouble angle;
+ gdouble x1, y1, x2, y2;
+
+ set_func = g_object_get_data (G_OBJECT (config), "set-func");
+ area = g_object_get_data (G_OBJECT (config), "area");
+
+ g_object_get (config,
+ "center-x", &x,
+ "center-y", &y,
+ "angle", &angle,
+ NULL);
+
+ angle = angle / 180.0 * G_PI;
+
+ x1 = x * area->width;
+ y1 = y * area->height;
+ x2 = x1 + cos (angle) * 100;
+ y2 = y1 - sin (angle) * 100;
+
+ set_func (set_data, area, x1, y1, x2, y2);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_motion_blur_circular (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *vbox;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs, n_param_specs,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+
+
+ if (create_controller_func)
+ {
+ GCallback set_func;
+ gpointer set_data;
+
+ set_func = create_controller_func (creator,
+ GIMP_CONTROLLER_TYPE_LINE,
+ _("Circular Motion Blur: "),
+ (GCallback) line_callback,
+ config,
+ &set_data);
+
+ g_object_set_data (G_OBJECT (config), "set-func", set_func);
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ config_notify (config, NULL, set_data);
+
+ g_signal_connect (config, "notify",
+ G_CALLBACK (config_notify),
+ set_data);
+ }
+
+ return vbox;
+}
diff --git a/app/propgui/gimppropgui-motion-blur-circular.h b/app/propgui/gimppropgui-motion-blur-circular.h
new file mode 100644
index 0000000..3bb851e
--- /dev/null
+++ b/app/propgui/gimppropgui-motion-blur-circular.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-motion-blur-circular.h
+ *
+ * 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_PROP_GUI_MOTION_BLUR_CIRCULAR_H__
+#define __GIMP_PROP_GUI_MOTION_BLUR_CIRCULAR_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_motion_blur_circular (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_MOTION_BLUR_CIRCULAR_H__ */
diff --git a/app/propgui/gimppropgui-motion-blur-linear.c b/app/propgui/gimppropgui-motion-blur-linear.c
new file mode 100644
index 0000000..ed917aa
--- /dev/null
+++ b/app/propgui/gimppropgui-motion-blur-linear.c
@@ -0,0 +1,145 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-motion-blur-linear.c
+ * Copyright (C) 2019 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui-generic.h"
+#include "gimppropgui-motion-blur-linear.h"
+
+#include "gimp-intl.h"
+
+
+static void
+line_callback (GObject *config,
+ GeglRectangle *area,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+ gdouble length;
+ gdouble angle;
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ length = sqrt (SQR (x2 - x1) + SQR (y2 - y1));
+ angle = atan2 (y2 - y1, x2 - x1);
+
+ angle = angle / G_PI * 180.0;
+
+ g_object_set (config,
+ "length", length,
+ "angle", angle,
+ NULL);
+}
+
+static void
+config_notify (GObject *config,
+ const GParamSpec *pspec,
+ gpointer set_data)
+{
+ GimpControllerLineCallback set_func;
+ GeglRectangle *area;
+ gdouble length;
+ gdouble angle;
+ gdouble x1, y1, x2, y2;
+
+ set_func = g_object_get_data (G_OBJECT (config), "set-func");
+ area = g_object_get_data (G_OBJECT (config), "area");
+
+ g_object_get (config,
+ "length", &length,
+ "angle", &angle,
+ NULL);
+
+ angle = angle / 180.0 * G_PI;
+
+ x1 = area->x + area->width / 2.0;
+ y1 = area->x + area->height / 2.0;
+ x2 = x1 + cos (angle) * length;
+ y2 = y1 + sin (angle) * length;
+
+ set_func (set_data, area, x1, y1, x2, y2);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_motion_blur_linear (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *vbox;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs, n_param_specs,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+
+
+ if (create_controller_func)
+ {
+ GCallback set_func;
+ gpointer set_data;
+
+ set_func = create_controller_func (creator,
+ GIMP_CONTROLLER_TYPE_LINE,
+ _("Linear Motion Blur: "),
+ (GCallback) line_callback,
+ config,
+ &set_data);
+
+ g_object_set_data (G_OBJECT (config), "set-func", set_func);
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ config_notify (config, NULL, set_data);
+
+ g_signal_connect (config, "notify",
+ G_CALLBACK (config_notify),
+ set_data);
+ }
+
+ return vbox;
+}
diff --git a/app/propgui/gimppropgui-motion-blur-linear.h b/app/propgui/gimppropgui-motion-blur-linear.h
new file mode 100644
index 0000000..aacb528
--- /dev/null
+++ b/app/propgui/gimppropgui-motion-blur-linear.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-motion-blur-linear.h
+ *
+ * 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_PROP_GUI_MOTION_BLUR_LINEAR_H__
+#define __GIMP_PROP_GUI_MOTION_BLUR_LINEAR_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_motion_blur_linear (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_MOTION_BLUR_LINEAR_H__ */
diff --git a/app/propgui/gimppropgui-motion-blur-zoom.c b/app/propgui/gimppropgui-motion-blur-zoom.c
new file mode 100644
index 0000000..e8e359b
--- /dev/null
+++ b/app/propgui/gimppropgui-motion-blur-zoom.c
@@ -0,0 +1,146 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-motion-blur-zoom.c
+ * Copyright (C) 2019 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui-generic.h"
+#include "gimppropgui-motion-blur-zoom.h"
+
+#include "gimp-intl.h"
+
+
+static void
+line_callback (GObject *config,
+ GeglRectangle *area,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+ gdouble x, y;
+ gdouble radius;
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ x = x1 / area->width;
+ y = y1 / area->height;
+ radius = x2 - x1;
+
+ radius = CLAMP (radius / 100.0, -0.5, 1.0);
+
+ g_object_set (config,
+ "center-x", x,
+ "center-y", y,
+ "factor", radius,
+ NULL);
+}
+
+static void
+config_notify (GObject *config,
+ const GParamSpec *pspec,
+ gpointer set_data)
+{
+ GimpControllerLineCallback set_func;
+ GeglRectangle *area;
+ gdouble x, y;
+ gdouble radius;
+ gdouble x1, y1, x2, y2;
+
+ set_func = g_object_get_data (G_OBJECT (config), "set-func");
+ area = g_object_get_data (G_OBJECT (config), "area");
+
+ g_object_get (config,
+ "center-x", &x,
+ "center-y", &y,
+ "factor", &radius,
+ NULL);
+
+ x1 = x * area->width;
+ y1 = y * area->height;
+ x2 = x1 + radius * 100.0;
+ y2 = y1;
+
+ set_func (set_data, area, x1, y1, x2, y2);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_motion_blur_zoom (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *vbox;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs, n_param_specs,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+
+
+ if (create_controller_func)
+ {
+ GCallback set_func;
+ gpointer set_data;
+
+ set_func = create_controller_func (creator,
+ GIMP_CONTROLLER_TYPE_LINE,
+ _("Zoom Motion Blur: "),
+ (GCallback) line_callback,
+ config,
+ &set_data);
+
+ g_object_set_data (G_OBJECT (config), "set-func", set_func);
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ config_notify (config, NULL, set_data);
+
+ g_signal_connect (config, "notify",
+ G_CALLBACK (config_notify),
+ set_data);
+ }
+
+ return vbox;
+}
diff --git a/app/propgui/gimppropgui-motion-blur-zoom.h b/app/propgui/gimppropgui-motion-blur-zoom.h
new file mode 100644
index 0000000..ab38304
--- /dev/null
+++ b/app/propgui/gimppropgui-motion-blur-zoom.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-motion-blur-zoom.h
+ *
+ * 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_PROP_GUI_MOTION_BLUR_ZOOM_H__
+#define __GIMP_PROP_GUI_MOTION_BLUR_ZOOM_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_motion_blur_zoom (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_MOTION_BLUR_ZOOM_H__ */
diff --git a/app/propgui/gimppropgui-newsprint.c b/app/propgui/gimppropgui-newsprint.c
new file mode 100644
index 0000000..214c534
--- /dev/null
+++ b/app/propgui/gimppropgui-newsprint.c
@@ -0,0 +1,444 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-newsprint.c
+ * Copyright (C) 2019 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-generic.h"
+#include "gimppropgui-newsprint.h"
+
+#include "gimp-intl.h"
+
+
+typedef enum
+{
+ COLOR_MODEL_WHITE_ON_BLACK,
+ COLOR_MODEL_BLACK_ON_WHITE,
+ COLOR_MODEL_RGB,
+ COLOR_MODEL_CMYK,
+
+ N_COLOR_MODELS
+} ColorModel;
+
+typedef enum
+{
+ PATTERN_LINE,
+ PATTERN_CIRCLE,
+ PATTERN_DIAMOND,
+ PATTERN_PSCIRCLE,
+ PATTERN_CROSS,
+
+ N_PATTERNS
+} Pattern;
+
+
+typedef struct _Newsprint Newsprint;
+
+struct _Newsprint
+{
+ GObject *config;
+ GtkWidget *notebook;
+ GtkWidget *pattern_check;
+ GtkWidget *period_check;
+ GtkWidget *angle_check;
+};
+
+
+/* local function prototypes */
+
+static void newsprint_color_model_notify (GObject *config,
+ const GParamSpec *pspec,
+ GtkWidget *label);
+static void newsprint_model_prop_notify (GObject *config,
+ const GParamSpec *pspec,
+ Newsprint *np);
+static void newsprint_lock_patterns_toggled (GtkWidget *widget,
+ Newsprint *np);
+static void newsprint_lock_periods_toggled (GtkWidget *widget,
+ Newsprint *np);
+static void newsprint_lock_angles_toggled (GtkWidget *widget,
+ Newsprint *np);
+
+
+static const gchar *label_strings[N_COLOR_MODELS][4] =
+{
+ { NULL, NULL, NULL, N_("White") },
+ { NULL, NULL, NULL, N_("Black") },
+ { N_("Red"), N_("Green"), N_("Blue"), NULL },
+ { N_("Cyan"), N_("Magenta"), N_("Yellow"), N_("Black") }
+};
+
+static const gchar *pattern_props[] =
+{
+ "pattern2",
+ "pattern3",
+ "pattern4",
+ "pattern"
+};
+
+static const gchar *period_props[] =
+{
+ "period2",
+ "period3",
+ "period4",
+ "period"
+};
+
+static const gchar *angle_props[] =
+{
+ "angle2",
+ "angle3",
+ "angle4",
+ "angle"
+};
+
+
+/* public functions */
+
+GtkWidget *
+_gimp_prop_gui_new_newsprint (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ Newsprint *np;
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *combo;
+ GtkWidget *labels[4];
+ GtkWidget *pages[4];
+ GtkWidget *check;
+ gint i;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ np = g_new0 (Newsprint, 1);
+
+ np->config = config;
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+
+ g_object_set_data_full (G_OBJECT (main_vbox), "newsprint", np,
+ (GDestroyNotify) g_free);
+
+ frame = gimp_frame_new (_("Channels"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ combo = _gimp_prop_gui_new_generic (config,
+ param_specs + 0, 1,
+ area,
+ context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
+ gtk_widget_show (combo);
+
+ np->notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), np->notebook, FALSE, FALSE, 0);
+ gtk_widget_show (np->notebook);
+
+ for (i = 0; i < 4; i++)
+ {
+ GtkWidget *widget;
+ const gchar *unused;
+ gint remaining;
+
+ labels[i] = gtk_label_new (NULL);
+ pages[i] = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+
+ g_object_set_data (G_OBJECT (labels[i]), "channel", GINT_TO_POINTER (i));
+
+ g_signal_connect_object (config, "notify::color-model",
+ G_CALLBACK (newsprint_color_model_notify),
+ G_OBJECT (labels[i]), 0);
+
+ newsprint_color_model_notify (config, NULL, labels[i]);
+
+ gtk_container_set_border_width (GTK_CONTAINER (pages[i]), 6);
+ gtk_notebook_append_page (GTK_NOTEBOOK (np->notebook),
+ pages[i], labels[i]);
+
+ widget = gimp_prop_widget_new_from_pspec (config,
+ param_specs[1 + 3 * i],
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator, &unused);
+ gtk_box_pack_start (GTK_BOX (pages[i]), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ g_object_bind_property (G_OBJECT (widget), "visible",
+ G_OBJECT (pages[i]), "visible",
+ G_BINDING_SYNC_CREATE);
+
+ if (i == 3)
+ remaining = 3;
+ else
+ remaining = 2;
+
+ widget = _gimp_prop_gui_new_generic (config,
+ param_specs + 2 + 3 * i, remaining,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_box_pack_start (GTK_BOX (pages[i]), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+ }
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ np->pattern_check = check =
+ gtk_check_button_new_with_mnemonic (_("_Lock patterns"));
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "toggled",
+ G_CALLBACK (newsprint_lock_patterns_toggled),
+ np);
+
+ np->period_check = check =
+ gtk_check_button_new_with_mnemonic (_("Loc_k periods"));
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "toggled",
+ G_CALLBACK (newsprint_lock_periods_toggled),
+ np);
+
+ np->angle_check = check =
+ gtk_check_button_new_with_mnemonic (_("Lock a_ngles"));
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "toggled",
+ G_CALLBACK (newsprint_lock_angles_toggled),
+ np);
+
+ frame = gimp_frame_new (_("Quality"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs + 14, 1,
+ area,
+ context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ frame = gimp_frame_new (_("Effects"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs + 15, 3,
+ area,
+ context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ g_signal_connect (config, "notify",
+ G_CALLBACK (newsprint_model_prop_notify),
+ np);
+
+ return main_vbox;
+}
+
+
+/* private functions */
+
+static void
+newsprint_color_model_notify (GObject *config,
+ const GParamSpec *pspec,
+ GtkWidget *label)
+{
+ ColorModel model;
+ gint channel;
+
+ g_object_get (config,
+ "color-model", &model,
+ NULL);
+
+ channel =
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (label), "channel"));
+
+ if (label_strings[model][channel])
+ gtk_label_set_text (GTK_LABEL (label),
+ gettext (label_strings[model][channel]));
+}
+
+static gboolean
+newsprint_sync_model_props (Newsprint *np,
+ const GParamSpec *pspec,
+ const gchar **props,
+ gint n_props)
+{
+ gint i;
+
+ for (i = 0; i < n_props; i++)
+ {
+ if (! strcmp (pspec->name, props[i]))
+ {
+ GValue value = G_VALUE_INIT;
+ gint j;
+
+ g_value_init (&value, pspec->value_type);
+
+ g_object_get_property (np->config, pspec->name, &value);
+
+ for (j = 0; j < n_props; j++)
+ {
+ if (i != j)
+ {
+ g_signal_handlers_block_by_func (np->config,
+ newsprint_model_prop_notify,
+ np);
+
+ g_object_set_property (np->config, props[j], &value);
+
+ g_signal_handlers_unblock_by_func (np->config,
+ newsprint_model_prop_notify,
+ np);
+ }
+ }
+
+ g_value_unset (&value);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+newsprint_model_prop_notify (GObject *config,
+ const GParamSpec *pspec,
+ Newsprint *np)
+{
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (np->pattern_check)) &&
+ newsprint_sync_model_props (np, pspec,
+ pattern_props, G_N_ELEMENTS (pattern_props)))
+ return;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (np->period_check)) &&
+ newsprint_sync_model_props (np, pspec,
+ period_props, G_N_ELEMENTS (period_props)))
+ return;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (np->angle_check)) &&
+ newsprint_sync_model_props (np, pspec,
+ angle_props, G_N_ELEMENTS (angle_props)))
+ return;
+}
+
+static void
+newsprint_lock_patterns_toggled (GtkWidget *widget,
+ Newsprint *np)
+
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ GParamSpec *pspec;
+ gint channel;
+
+ channel = gtk_notebook_get_current_page (GTK_NOTEBOOK (np->notebook));
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (np->config),
+ pattern_props[channel]);
+
+ newsprint_sync_model_props (np, pspec,
+ pattern_props, G_N_ELEMENTS (pattern_props));
+ }
+}
+
+static void
+newsprint_lock_periods_toggled (GtkWidget *widget,
+ Newsprint *np)
+
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ GParamSpec *pspec;
+ gint channel;
+
+ channel = gtk_notebook_get_current_page (GTK_NOTEBOOK (np->notebook));
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (np->config),
+ period_props[channel]);
+
+ newsprint_sync_model_props (np, pspec,
+ period_props, G_N_ELEMENTS (period_props));
+ }
+}
+
+static void
+newsprint_lock_angles_toggled (GtkWidget *widget,
+ Newsprint *np)
+
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ GParamSpec *pspec;
+ gint channel;
+
+ channel = gtk_notebook_get_current_page (GTK_NOTEBOOK (np->notebook));
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (np->config),
+ angle_props[channel]);
+
+ newsprint_sync_model_props (np, pspec,
+ angle_props, G_N_ELEMENTS (angle_props));
+ }
+}
diff --git a/app/propgui/gimppropgui-newsprint.h b/app/propgui/gimppropgui-newsprint.h
new file mode 100644
index 0000000..90df485
--- /dev/null
+++ b/app/propgui/gimppropgui-newsprint.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-newsprint.h
+ *
+ * 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_PROP_GUI_NEWSPRINT_H__
+#define __GIMP_PROP_GUI_NEWSPRINT_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_newsprint (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_NEWSPRINT_H__ */
diff --git a/app/propgui/gimppropgui-panorama-projection.c b/app/propgui/gimppropgui-panorama-projection.c
new file mode 100644
index 0000000..e5aed5b
--- /dev/null
+++ b/app/propgui/gimppropgui-panorama-projection.c
@@ -0,0 +1,144 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-panorama-projection.c
+ * Copyright (C) 2018 Ell
+ *
+ * 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-generic.h"
+#include "gimppropgui-panorama-projection.h"
+
+#include "gimp-intl.h"
+
+
+static void
+gyroscope_callback (GObject *config,
+ GeglRectangle *area,
+ gdouble yaw,
+ gdouble pitch,
+ gdouble roll,
+ gdouble zoom,
+ gboolean invert)
+{
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ g_object_set (config,
+ "pan", -yaw,
+ "tilt", -pitch,
+ "spin", -roll,
+ "zoom", CLAMP (100.0 * zoom, 0.01, 1000.0),
+ "inverse", invert,
+ NULL);
+}
+
+static void
+config_notify (GObject *config,
+ const GParamSpec *pspec,
+ gpointer set_data)
+{
+ GimpControllerGyroscopeCallback set_func;
+ GeglRectangle *area;
+ gdouble pan;
+ gdouble tilt;
+ gdouble spin;
+ gdouble zoom;
+ gboolean inverse;
+
+ set_func = g_object_get_data (G_OBJECT (config), "set-func");
+ area = g_object_get_data (G_OBJECT (config), "area");
+
+ g_object_get (config,
+ "pan", &pan,
+ "tilt", &tilt,
+ "spin", &spin,
+ "zoom", &zoom,
+ "inverse", &inverse,
+ NULL);
+
+ set_func (set_data,
+ area,
+ -pan, -tilt, -spin,
+ zoom / 100.0,
+ inverse);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_panorama_projection (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *vbox;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs, n_param_specs,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+
+
+ if (create_controller_func)
+ {
+ GCallback set_func;
+ gpointer set_data;
+
+ set_func = create_controller_func (creator,
+ GIMP_CONTROLLER_TYPE_GYROSCOPE,
+ _("Panorama Projection: "),
+ (GCallback) gyroscope_callback,
+ config,
+ &set_data);
+
+ g_object_set_data (G_OBJECT (config), "set-func", set_func);
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ config_notify (config, NULL, set_data);
+
+ g_signal_connect (config, "notify",
+ G_CALLBACK (config_notify),
+ set_data);
+ }
+
+ return vbox;
+}
diff --git a/app/propgui/gimppropgui-panorama-projection.h b/app/propgui/gimppropgui-panorama-projection.h
new file mode 100644
index 0000000..11c7b3a
--- /dev/null
+++ b/app/propgui/gimppropgui-panorama-projection.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-panorama-projection.h
+ *
+ * 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_PROP_GUI_PANORAMA_PROJECTION_H__
+#define __GIMP_PROP_GUI_PANORAMA_PROJECTION_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_panorama_projection (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_PANORAMA_PROJECTION_H__ */
diff --git a/app/propgui/gimppropgui-recursive-transform.c b/app/propgui/gimppropgui-recursive-transform.c
new file mode 100644
index 0000000..a247723
--- /dev/null
+++ b/app/propgui/gimppropgui-recursive-transform.c
@@ -0,0 +1,334 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-recursive-transform.c
+ * Copyright (C) 2018 Ell
+ *
+ * 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-generic.h"
+#include "gimppropgui-recursive-transform.h"
+
+#include "gimp-intl.h"
+
+
+#define MAX_N_TRANSFORMS 10
+
+
+static void
+add_transform (GtkButton *button,
+ GObject *config)
+{
+ gchar *transform;
+ gchar *new_transform;
+
+ g_object_get (config,
+ "transform", &transform,
+ NULL);
+
+ new_transform = g_strdup_printf ("%s;matrix (1, 0, 0, 0, 1, 0, 0, 0, 1)",
+ transform);
+
+ g_object_set (config,
+ "transform", new_transform,
+ NULL);
+
+ g_free (transform);
+ g_free (new_transform);
+}
+
+static void
+duplicate_transform (GtkButton *button,
+ GObject *config)
+{
+ gchar *transform;
+ gchar *new_transform;
+ gchar *delim;
+
+ g_object_get (config,
+ "transform", &transform,
+ NULL);
+
+ delim = strrchr (transform, ';');
+
+ if (! delim)
+ delim = transform;
+ else
+ delim++;
+
+ new_transform = g_strdup_printf ("%s;%s", transform, delim);
+
+ g_object_set (config,
+ "transform", new_transform,
+ NULL);
+
+ g_free (transform);
+ g_free (new_transform);
+}
+
+static void
+remove_transform (GtkButton *button,
+ GObject *config)
+{
+ gchar *transform;
+ gchar *delim;
+
+ g_object_get (config,
+ "transform", &transform,
+ NULL);
+
+ delim = strrchr (transform, ';');
+
+ if (delim)
+ {
+ *delim = '\0';
+
+ g_object_set (config,
+ "transform", transform,
+ NULL);
+ }
+
+ g_free (transform);
+}
+
+static void
+transform_grids_callback (GObject *config,
+ GeglRectangle *area,
+ const GimpMatrix3 *transforms,
+ gint n_transforms)
+{
+ GString *transforms_str = g_string_new (NULL);
+ gchar *transform_str;
+ gint i;
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ for (i = 0; i < n_transforms; i++)
+ {
+ if (i > 0)
+ g_string_append (transforms_str, ";");
+
+ transform_str = gegl_matrix3_to_string ((GeglMatrix3 *) &transforms[i]);
+
+ g_string_append (transforms_str, transform_str);
+
+ g_free (transform_str);
+ }
+
+ transform_str = g_string_free (transforms_str, FALSE);
+
+ g_object_set (config,
+ "transform", transform_str,
+ NULL);
+
+ g_free (transform_str);
+}
+
+static void
+config_notify (GObject *config,
+ const GParamSpec *pspec,
+ gpointer set_data)
+{
+ GtkWidget *add_button;
+ GtkWidget *duplicate_button;
+ GtkWidget *remove_button;
+ GimpControllerTransformGridsCallback set_func;
+ GeglRectangle *area;
+ gchar *transform_str;
+ gchar **transform_strs;
+ GimpMatrix3 *transforms;
+ gint n_transforms;
+ gint i;
+
+ add_button = g_object_get_data (G_OBJECT (config), "add-transform-button");
+ duplicate_button = g_object_get_data (G_OBJECT (config), "duplicate-transform-button");
+ remove_button = g_object_get_data (G_OBJECT (config), "remove-transform-button");
+ set_func = g_object_get_data (G_OBJECT (config), "set-func");
+ area = g_object_get_data (G_OBJECT (config), "area");
+
+ g_object_get (config,
+ "transform", &transform_str,
+ NULL);
+
+ transform_strs = g_strsplit (transform_str, ";", -1);
+
+ g_free (transform_str);
+
+ for (n_transforms = 0; transform_strs[n_transforms]; n_transforms++);
+
+ transforms = g_new (GimpMatrix3, n_transforms);
+
+ for (i = 0; i < n_transforms; i++)
+ {
+ gegl_matrix3_parse_string ((GeglMatrix3 *) &transforms[i],
+ transform_strs[i]);
+ }
+
+ set_func (set_data, area, transforms, n_transforms);
+
+ g_strfreev (transform_strs);
+ g_free (transforms);
+
+ gtk_widget_set_sensitive (add_button, n_transforms < MAX_N_TRANSFORMS);
+ gtk_widget_set_sensitive (duplicate_button, n_transforms < MAX_N_TRANSFORMS);
+ gtk_widget_set_sensitive (remove_button, n_transforms > 1);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_recursive_transform (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *vbox;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ /* skip the "transform" property, which is controlled by a transform-grid
+ * controller.
+ */
+ if (create_controller_func)
+ {
+ param_specs++;
+ n_param_specs--;
+ }
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs, n_param_specs,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+
+ if (create_controller_func)
+ {
+ GtkWidget *outer_vbox;
+ GtkWidget *hbox;
+ GtkWidget *button;
+ GtkWidget *image;
+ GCallback set_func;
+ gpointer set_data;
+
+ outer_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL,
+ gtk_box_get_spacing (GTK_BOX (vbox)));
+
+ gtk_box_pack_start (GTK_BOX (outer_vbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (outer_vbox), hbox, FALSE, FALSE, 2);
+ gtk_widget_show (hbox);
+
+ button = gtk_button_new ();
+ gimp_help_set_help_data (button,
+ _("Add transform"),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_LIST_ADD,
+ GTK_ICON_SIZE_MENU);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (add_transform),
+ config);
+
+ g_object_set_data (config, "add-transform-button", button);
+
+ button = gtk_button_new ();
+ gimp_help_set_help_data (button,
+ _("Duplicate transform"),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_OBJECT_DUPLICATE,
+ GTK_ICON_SIZE_MENU);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (duplicate_transform),
+ config);
+
+ g_object_set_data (config, "duplicate-transform-button", button);
+
+ button = gtk_button_new ();
+ gimp_help_set_help_data (button,
+ _("Remove transform"),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_LIST_REMOVE,
+ GTK_ICON_SIZE_MENU);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (remove_transform),
+ config);
+
+ g_object_set_data (config, "remove-transform-button", button);
+
+ vbox = outer_vbox;
+
+ set_func = create_controller_func (creator,
+ GIMP_CONTROLLER_TYPE_TRANSFORM_GRIDS,
+ _("Recursive Transform: "),
+ (GCallback) transform_grids_callback,
+ config,
+ &set_data);
+
+ g_object_set_data (G_OBJECT (config), "set-func", set_func);
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ config_notify (config, NULL, set_data);
+
+ g_signal_connect (config, "notify",
+ G_CALLBACK (config_notify),
+ set_data);
+ }
+
+ return vbox;
+}
diff --git a/app/propgui/gimppropgui-recursive-transform.h b/app/propgui/gimppropgui-recursive-transform.h
new file mode 100644
index 0000000..ac75545
--- /dev/null
+++ b/app/propgui/gimppropgui-recursive-transform.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-recursive-transform.h
+ *
+ * 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_PROP_GUI_RECURSIVE_TRANSFORM_H__
+#define __GIMP_PROP_GUI_RECURSIVE_TRANSFORM_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_recursive_transform (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_RECURSIVE_TRANSFORM_H__ */
diff --git a/app/propgui/gimppropgui-shadows-highlights.c b/app/propgui/gimppropgui-shadows-highlights.c
new file mode 100644
index 0000000..a5601a0
--- /dev/null
+++ b/app/propgui/gimppropgui-shadows-highlights.c
@@ -0,0 +1,122 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-shadows-highlights.c
+ * Copyright (C) 2002-2014 Michael Natterer <mitch@gimp.org>
+ * 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 "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-shadows-highlights.h"
+
+#include "gimp-intl.h"
+
+
+GtkWidget *
+_gimp_prop_gui_new_shadows_highlights (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *scale;
+ const gchar *label;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+
+ frame = gimp_frame_new (_("Shadows"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ scale = gimp_prop_widget_new (config, "shadows",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "shadows-ccorrect",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ frame = gimp_frame_new (_("Highlights"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ scale = gimp_prop_widget_new (config, "highlights",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "highlights-ccorrect",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ frame = gimp_frame_new (_("Common"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ scale = gimp_prop_widget_new (config, "whitepoint",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "radius",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ scale = gimp_prop_widget_new (config, "compress",
+ area, context, NULL, NULL, NULL, &label);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ return main_vbox;
+}
diff --git a/app/propgui/gimppropgui-shadows-highlights.h b/app/propgui/gimppropgui-shadows-highlights.h
new file mode 100644
index 0000000..fee9b52
--- /dev/null
+++ b/app/propgui/gimppropgui-shadows-highlights.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-shadows-highlights.h
+ *
+ * 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_PROP_GUI_SHADOWS_HIGHLIGHTS_H__
+#define __GIMP_PROP_GUI_SHADOWS_HIGHLIGHTS_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_shadows_highlights (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_SHADOWS_HIGHLIGHTS_H__ */
diff --git a/app/propgui/gimppropgui-spiral.c b/app/propgui/gimppropgui-spiral.c
new file mode 100644
index 0000000..9aaded2
--- /dev/null
+++ b/app/propgui/gimppropgui-spiral.c
@@ -0,0 +1,239 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-spiral.c
+ * Copyright (C) 2017 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-generic.h"
+#include "gimppropgui-spiral.h"
+
+#include "gimp-intl.h"
+
+
+typedef enum
+{
+ GEGL_SPIRAL_TYPE_LINEAR,
+ GEGL_SPIRAL_TYPE_LOGARITHMIC
+} GeglSpiralType;
+
+
+static void
+slider_line_callback (GObject *config,
+ GeglRectangle *area,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ const GimpControllerSlider *sliders,
+ gint n_sliders)
+{
+ GeglSpiralType type;
+ gdouble x, y;
+ gdouble radius;
+ gdouble rotation;
+ gdouble base;
+ gdouble balance;
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ g_object_get (config,
+ "type", &type,
+ "base", &base,
+ "balance", &balance,
+ NULL);
+
+ x = x1 / area->width;
+ y = y1 / area->height;
+ radius = sqrt (SQR (x2 - x1) + SQR (y2 - y1));
+ rotation = atan2 (-(y2 - y1), x2 - x1) * 180 / G_PI;
+
+ if (rotation < 0)
+ rotation += 360.0;
+
+ switch (type)
+ {
+ case GEGL_SPIRAL_TYPE_LINEAR:
+ balance = 3.0 - 4.0 * sliders[0].value;
+
+ break;
+
+ case GEGL_SPIRAL_TYPE_LOGARITHMIC:
+ {
+ gdouble old_base = base;
+
+ base = 1.0 / sliders[1].value;
+ base = MIN (base, 1000000.0);
+
+ /* keep "balance" fixed when changing "base", or when "base" is 1, in
+ * which case there's no inverse mapping for the slider value, and we
+ * can get NaN.
+ */
+ if (base == old_base && base > 1.0)
+ {
+ balance = -4.0 * log (sliders[0].value) / log (base) - 1.0;
+ balance = CLAMP (balance, -1.0, 1.0);
+ }
+ }
+ break;
+ }
+
+ g_object_set (config,
+ "x", x,
+ "y", y,
+ "radius", radius,
+ "base", base,
+ "rotation", rotation,
+ "balance", balance,
+ NULL);
+}
+
+static void
+config_notify (GObject *config,
+ const GParamSpec *pspec,
+ gpointer set_data)
+{
+ GimpControllerSliderLineCallback set_func;
+ GeglRectangle *area;
+ GeglSpiralType type;
+ gdouble x, y;
+ gdouble radius;
+ gdouble rotation;
+ gdouble base;
+ gdouble balance;
+ gdouble x1, y1, x2, y2;
+ GimpControllerSlider sliders[2];
+ gint n_sliders = 0;
+
+ set_func = g_object_get_data (G_OBJECT (config), "set-func");
+ area = g_object_get_data (G_OBJECT (config), "area");
+
+ g_object_get (config,
+ "type", &type,
+ "x", &x,
+ "y", &y,
+ "radius", &radius,
+ "rotation", &rotation,
+ "base", &base,
+ "balance", &balance,
+ NULL);
+
+ x1 = x * area->width;
+ y1 = y * area->height;
+ x2 = x1 + cos (rotation * (G_PI / 180.0)) * radius;
+ y2 = y1 - sin (rotation * (G_PI / 180.0)) * radius;
+
+ switch (type)
+ {
+ case GEGL_SPIRAL_TYPE_LINEAR:
+ n_sliders = 1;
+
+ /* balance */
+ sliders[0] = GIMP_CONTROLLER_SLIDER_DEFAULT;
+ sliders[0].min = 0.5;
+ sliders[0].max = 1.0;
+ sliders[0].value = 0.5 + (1.0 - balance) / 4.0;
+
+ break;
+
+ case GEGL_SPIRAL_TYPE_LOGARITHMIC:
+ n_sliders = 2;
+
+ /* balance */
+ sliders[0] = GIMP_CONTROLLER_SLIDER_DEFAULT;
+ sliders[0].min = 1.0 / sqrt (base);
+ sliders[0].max = 1.0;
+ sliders[0].value = pow (base, -(balance + 1.0) / 4.0);
+
+ /* base */
+ sliders[1] = GIMP_CONTROLLER_SLIDER_DEFAULT;
+ sliders[1].min = 0.0;
+ sliders[1].max = 1.0;
+ sliders[1].value = 1.0 / base;
+
+ break;
+ }
+
+ set_func (set_data, area, x1, y1, x2, y2, sliders, n_sliders);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_spiral (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *vbox;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs, n_param_specs,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+
+
+ if (create_controller_func)
+ {
+ GCallback set_func;
+ gpointer set_data;
+
+ set_func = create_controller_func (creator,
+ GIMP_CONTROLLER_TYPE_SLIDER_LINE,
+ _("Spiral: "),
+ (GCallback) slider_line_callback,
+ config,
+ &set_data);
+
+ g_object_set_data (G_OBJECT (config), "set-func", set_func);
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ config_notify (config, NULL, set_data);
+
+ g_signal_connect (config, "notify",
+ G_CALLBACK (config_notify),
+ set_data);
+ }
+
+ return vbox;
+}
diff --git a/app/propgui/gimppropgui-spiral.h b/app/propgui/gimppropgui-spiral.h
new file mode 100644
index 0000000..1ed47df
--- /dev/null
+++ b/app/propgui/gimppropgui-spiral.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-spiral.h
+ *
+ * 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_PROP_GUI_SPIRAL_H__
+#define __GIMP_PROP_GUI_SPIRAL_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_spiral (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_SPIRAL_H__ */
diff --git a/app/propgui/gimppropgui-supernova.c b/app/propgui/gimppropgui-supernova.c
new file mode 100644
index 0000000..8cc0855
--- /dev/null
+++ b/app/propgui/gimppropgui-supernova.c
@@ -0,0 +1,144 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-supernova.c
+ * Copyright (C) 2017 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui-generic.h"
+#include "gimppropgui-supernova.h"
+
+#include "gimp-intl.h"
+
+
+static void
+line_callback (GObject *config,
+ GeglRectangle *area,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+ gdouble x, y;
+ gint radius;
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ x = x1 / area->width;
+ y = y1 / area->height;
+ radius = sqrt (SQR (x2 - x1) + SQR (y2 - y1));
+
+ g_object_set (config,
+ "center-x", x,
+ "center-y", y,
+ "radius", radius,
+ NULL);
+}
+
+static void
+config_notify (GObject *config,
+ const GParamSpec *pspec,
+ gpointer set_data)
+{
+ GimpControllerLineCallback set_func;
+ GeglRectangle *area;
+ gdouble x, y;
+ gint radius;
+ gdouble x1, y1, x2, y2;
+
+ set_func = g_object_get_data (G_OBJECT (config), "set-func");
+ area = g_object_get_data (G_OBJECT (config), "area");
+
+ g_object_get (config,
+ "center-x", &x,
+ "center-y", &y,
+ "radius", &radius,
+ NULL);
+
+ x1 = x * area->width;
+ y1 = y * area->height;
+ x2 = x1 + radius;
+ y2 = y1;
+
+ set_func (set_data, area, x1, y1, x2, y2);
+}
+
+GtkWidget *
+_gimp_prop_gui_new_supernova (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *vbox;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs, n_param_specs,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+
+
+ if (create_controller_func)
+ {
+ GCallback set_func;
+ gpointer set_data;
+
+ set_func = create_controller_func (creator,
+ GIMP_CONTROLLER_TYPE_LINE,
+ _("Supernova: "),
+ (GCallback) line_callback,
+ config,
+ &set_data);
+
+ g_object_set_data (G_OBJECT (config), "set-func", set_func);
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ config_notify (config, NULL, set_data);
+
+ g_signal_connect (config, "notify",
+ G_CALLBACK (config_notify),
+ set_data);
+ }
+
+ return vbox;
+}
diff --git a/app/propgui/gimppropgui-supernova.h b/app/propgui/gimppropgui-supernova.h
new file mode 100644
index 0000000..7cf3a42
--- /dev/null
+++ b/app/propgui/gimppropgui-supernova.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-supernova.h
+ *
+ * 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_PROP_GUI_SUPERNOVA_H__
+#define __GIMP_PROP_GUI_SUPERNOVA_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_supernova (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_SUPERNOVA_H__ */
diff --git a/app/propgui/gimppropgui-utils.c b/app/propgui/gimppropgui-utils.c
new file mode 100644
index 0000000..2721ba5
--- /dev/null
+++ b/app/propgui/gimppropgui-utils.c
@@ -0,0 +1,221 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-utils.c
+ * Copyright (C) 2002-2017 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "widgets/gimpwidgets-utils.h"
+
+#include "gimppropgui-utils.h"
+
+#include "gimp-intl.h"
+
+
+/* local function prototypes */
+
+static void gimp_prop_kelvin_presets_menu_position (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data);
+static gboolean gimp_prop_kelvin_presets_button_press (GtkWidget *widget,
+ GdkEventButton *bevent,
+ GtkMenu *menu);
+static void gimp_prop_kelvin_presets_activate (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_random_seed_new_clicked (GtkButton *button,
+ GtkAdjustment *adj);
+
+
+/* public functions */
+
+GtkWidget *
+gimp_prop_kelvin_presets_new (GObject *config,
+ const gchar *property_name)
+{
+ GtkWidget *button;
+ GtkWidget *menu;
+ gint i;
+
+ const struct
+ {
+ gdouble kelvin;
+ const gchar *label;
+ }
+ kelvin_presets[] =
+ {
+ { 1700, N_("1,700 K – Match flame") },
+ { 1850, N_("1,850 K – Candle flame, sunset/sunrise") },
+ { 2700, N_("2,700 K - Soft (or warm) LED lamps") },
+ { 3000, N_("3,000 K – Soft (or warm) white compact fluorescent lamps") },
+ { 3200, N_("3,200 K – Studio lamps, photofloods, etc.") },
+ { 3300, N_("3,300 K – Incandescent lamps") },
+ { 3350, N_("3,350 K – Studio \"CP\" light") },
+ { 4000, N_("4,000 K - Cold (daylight) LED lamps") },
+ { 4100, N_("4,100 K – Moonlight") },
+ { 5000, N_("5,000 K – D50") },
+ { 5000, N_("5,000 K – Cool white/daylight compact fluorescent lamps") },
+ { 5000, N_("5,000 K – Horizon daylight") },
+ { 5500, N_("5,500 K – D55") },
+ { 5500, N_("5,500 K – Vertical daylight, electronic flash") },
+ { 6200, N_("6,200 K – Xenon short-arc lamp") },
+ { 6500, N_("6,500 K – D65") },
+ { 6500, N_("6,500 K – Daylight, overcast") },
+ { 7500, N_("7,500 K – D75") },
+ { 9300, N_("9,300 K") }
+ };
+
+ button = gtk_button_new ();
+ gtk_widget_set_can_focus (button, FALSE);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+
+ gtk_button_set_image (GTK_BUTTON (button),
+ gtk_image_new_from_icon_name (GIMP_ICON_MENU_LEFT,
+ GTK_ICON_SIZE_MENU));
+
+ menu = gtk_menu_new ();
+ gtk_menu_attach_to_widget (GTK_MENU (menu), button, NULL);
+
+ gimp_help_set_help_data (button,
+ _("Choose from a list of common "
+ "color temperatures"), NULL);
+
+ g_signal_connect (button, "button-press-event",
+ G_CALLBACK (gimp_prop_kelvin_presets_button_press),
+ menu);
+
+ for (i = 0; i < G_N_ELEMENTS (kelvin_presets); i++)
+ {
+ GtkWidget *item;
+ gdouble *kelvin;
+
+ item = gtk_menu_item_new_with_label (gettext (kelvin_presets[i].label));
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+
+ g_object_set_data_full (G_OBJECT (item), "property-name",
+ g_strdup (property_name),
+ (GDestroyNotify) g_free);
+
+ kelvin = g_new (gdouble, 1);
+ *kelvin = kelvin_presets[i].kelvin;
+
+ g_object_set_data_full (G_OBJECT (item), "kelvin",
+ kelvin, (GDestroyNotify) g_free);
+
+ g_signal_connect (item, "activate",
+ G_CALLBACK (gimp_prop_kelvin_presets_activate),
+ config);
+
+ }
+
+ return button;
+}
+
+GtkWidget *
+gimp_prop_random_seed_new (GObject *config,
+ const gchar *property_name)
+{
+ GtkAdjustment *adj;
+ GtkWidget *hbox;
+ GtkWidget *spin;
+ GtkWidget *button;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ spin = gimp_prop_spin_button_new (config, property_name,
+ 1.0, 10.0, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0);
+ gtk_widget_show (spin);
+
+ button = gtk_button_new_with_label (_("New Seed"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin));
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (gimp_prop_random_seed_new_clicked),
+ adj);
+
+ return hbox;
+}
+
+
+/* private functions */
+
+static void
+gimp_prop_kelvin_presets_menu_position (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data)
+{
+ gimp_button_menu_position (user_data, menu, GTK_POS_LEFT, x, y);
+}
+
+static gboolean
+gimp_prop_kelvin_presets_button_press (GtkWidget *widget,
+ GdkEventButton *bevent,
+ GtkMenu *menu)
+{
+ if (bevent->type == GDK_BUTTON_PRESS)
+ {
+ gtk_menu_popup (menu,
+ NULL, NULL,
+ gimp_prop_kelvin_presets_menu_position, widget,
+ bevent->button, bevent->time);
+ }
+
+ return TRUE;
+}
+
+static void
+gimp_prop_kelvin_presets_activate (GtkWidget *widget,
+ GObject *config)
+{
+ const gchar *property_name;
+ gdouble *kelvin;
+
+ property_name = g_object_get_data (G_OBJECT (widget), "property-name");
+ kelvin = g_object_get_data (G_OBJECT (widget), "kelvin");
+
+ if (property_name && kelvin)
+ g_object_set (config, property_name, *kelvin, NULL);
+}
+
+static void
+gimp_prop_random_seed_new_clicked (GtkButton *button,
+ GtkAdjustment *adj)
+{
+ guint32 value;
+
+ value = floor (g_random_double_range (gtk_adjustment_get_lower (adj),
+ gtk_adjustment_get_upper (adj) + 1.0));
+
+ gtk_adjustment_set_value (adj, value);
+}
diff --git a/app/propgui/gimppropgui-utils.h b/app/propgui/gimppropgui-utils.h
new file mode 100644
index 0000000..f618ecf
--- /dev/null
+++ b/app/propgui/gimppropgui-utils.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-utils.h
+ * Copyright (C) 2002-2017 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_PROP_GUI_UTILS_H__
+#define __GIMP_PROP_GUI_UTILS_H__
+
+
+GtkWidget * gimp_prop_kelvin_presets_new (GObject *config,
+ const gchar *property_name);
+
+GtkWidget * gimp_prop_random_seed_new (GObject *config,
+ const gchar *property_name);
+
+
+#endif /* __GIMP_PROP_GUI_UTILS_H__ */
diff --git a/app/propgui/gimppropgui-vignette.c b/app/propgui/gimppropgui-vignette.c
new file mode 100644
index 0000000..5d8ef81
--- /dev/null
+++ b/app/propgui/gimppropgui-vignette.c
@@ -0,0 +1,202 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-vignette.c
+ * Copyright (C) 2020 Ell
+ *
+ * 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 "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "core/gimpcontext.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-generic.h"
+#include "gimppropgui-vignette.h"
+
+#include "gimp-intl.h"
+
+
+#define MAX_GAMMA 1000.0
+
+
+static void
+focus_callback (GObject *config,
+ GeglRectangle *area,
+ GimpLimitType type,
+ gdouble x,
+ gdouble y,
+ gdouble radius,
+ gdouble aspect_ratio,
+ gdouble angle,
+ gdouble inner_limit,
+ gdouble midpoint)
+{
+ gdouble proportion;
+ gdouble squeeze;
+ gdouble scale;
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ g_object_get (config,
+ "proportion", &proportion,
+ NULL);
+
+ if (aspect_ratio >= 0.0)
+ scale = 1.0 - aspect_ratio;
+ else
+ scale = 1.0 / (1.0 + aspect_ratio);
+
+ scale /= 1.0 + proportion * ((gdouble) area->height / area->width - 1.0);
+
+ if (scale <= 1.0)
+ squeeze = +2.0 * atan (1.0 / scale - 1.0) / G_PI;
+ else
+ squeeze = -2.0 * atan (scale - 1.0) / G_PI;
+
+ g_object_set (config,
+ "shape", type,
+ "x", x / area->width,
+ "y", y / area->height,
+ "radius", 2.0 * radius / area->width,
+ "proportion", proportion,
+ "squeeze", squeeze,
+ "rotation", fmod (fmod (angle * 180.0 / G_PI, 360.0) + 360.0,
+ 360.0),
+ "softness", 1.0 - inner_limit,
+ "gamma", midpoint < 1.0 ?
+ MIN (log (0.5) / log (midpoint), MAX_GAMMA) :
+ MAX_GAMMA,
+ NULL);
+}
+
+static void
+config_notify (GObject *config,
+ const GParamSpec *pspec,
+ gpointer set_data)
+{
+ GimpControllerFocusCallback set_func;
+ GeglRectangle *area;
+ GimpLimitType shape;
+ gdouble x, y;
+ gdouble radius;
+ gdouble proportion;
+ gdouble squeeze;
+ gdouble rotation;
+ gdouble softness;
+ gdouble gamma;
+ gdouble aspect_ratio;
+
+ set_func = g_object_get_data (G_OBJECT (config), "set-func");
+ area = g_object_get_data (G_OBJECT (config), "area");
+
+ g_object_get (config,
+ "shape", &shape,
+ "x", &x,
+ "y", &y,
+ "radius", &radius,
+ "proportion", &proportion,
+ "squeeze", &squeeze,
+ "rotation", &rotation,
+ "softness", &softness,
+ "gamma", &gamma,
+ NULL);
+
+ aspect_ratio = 1.0 + ((gdouble) area->height / area->width - 1.0) *
+ proportion;
+
+ if (squeeze >= 0.0)
+ aspect_ratio /= tan (+squeeze * G_PI / 2.0) + 1.0;
+ else
+ aspect_ratio *= tan (-squeeze * G_PI / 2.0) + 1.0;
+
+ if (aspect_ratio <= 1.0)
+ aspect_ratio = 1.0 - aspect_ratio;
+ else
+ aspect_ratio = 1.0 / aspect_ratio - 1.0;
+
+ set_func (set_data, area,
+ shape,
+ x * area->width,
+ y * area->height,
+ radius * area->width / 2.0,
+ aspect_ratio,
+ rotation / 180.0 * G_PI,
+ 1.0 - softness,
+ pow (0.5, 1.0 / gamma));
+}
+
+GtkWidget *
+_gimp_prop_gui_new_vignette (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *vbox;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (param_specs != NULL, NULL);
+ g_return_val_if_fail (n_param_specs > 0, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ vbox = _gimp_prop_gui_new_generic (config,
+ param_specs, n_param_specs,
+ area, context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+
+
+ if (create_controller_func)
+ {
+ GCallback set_func;
+ gpointer set_data;
+
+ set_func = create_controller_func (creator,
+ GIMP_CONTROLLER_TYPE_FOCUS,
+ _("Vignette: "),
+ (GCallback) focus_callback,
+ config,
+ &set_data);
+
+ g_object_set_data (G_OBJECT (config), "set-func", set_func);
+
+ g_object_set_data_full (G_OBJECT (config), "area",
+ g_memdup (area, sizeof (GeglRectangle)),
+ (GDestroyNotify) g_free);
+
+ config_notify (config, NULL, set_data);
+
+ g_signal_connect (config, "notify",
+ G_CALLBACK (config_notify),
+ set_data);
+ }
+
+ return vbox;
+}
diff --git a/app/propgui/gimppropgui-vignette.h b/app/propgui/gimppropgui-vignette.h
new file mode 100644
index 0000000..4db7ea4
--- /dev/null
+++ b/app/propgui/gimppropgui-vignette.h
@@ -0,0 +1,36 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui-vignette.h
+ * Copyright (C) 2020 Ell
+ *
+ * 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_PROP_GUI_VIGNETTE_H__
+#define __GIMP_PROP_GUI_VIGNETTE_H__
+
+
+GtkWidget *
+_gimp_prop_gui_new_vignette (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+
+#endif /* __GIMP_PROP_GUI_VIGNETTE_H__ */
diff --git a/app/propgui/gimppropgui.c b/app/propgui/gimppropgui.c
new file mode 100644
index 0000000..fcc9a7e
--- /dev/null
+++ b/app/propgui/gimppropgui.c
@@ -0,0 +1,702 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui.c
+ * Copyright (C) 2002-2017 Michael Natterer <mitch@gimp.org>
+ * 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 <string.h>
+
+#include <gegl.h>
+#include <gegl-paramspecs.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "propgui-types.h"
+
+#include "gegl/gimp-gegl-utils.h"
+
+#include "operations/gimp-operation-config.h"
+
+#include "core/gimpcontext.h"
+
+#include "widgets/gimpcolorpanel.h"
+#include "widgets/gimpmessagebox.h"
+#include "widgets/gimpspinscale.h"
+#include "widgets/gimppropwidgets.h"
+
+#include "gimppropgui.h"
+#include "gimppropgui-channel-mixer.h"
+#include "gimppropgui-color-balance.h"
+#include "gimppropgui-color-rotate.h"
+#include "gimppropgui-color-to-alpha.h"
+#include "gimppropgui-convolution-matrix.h"
+#include "gimppropgui-diffraction-patterns.h"
+#include "gimppropgui-eval.h"
+#include "gimppropgui-focus-blur.h"
+#include "gimppropgui-generic.h"
+#include "gimppropgui-hue-saturation.h"
+#include "gimppropgui-motion-blur-circular.h"
+#include "gimppropgui-motion-blur-linear.h"
+#include "gimppropgui-motion-blur-zoom.h"
+#include "gimppropgui-newsprint.h"
+#include "gimppropgui-panorama-projection.h"
+#include "gimppropgui-recursive-transform.h"
+#include "gimppropgui-shadows-highlights.h"
+#include "gimppropgui-spiral.h"
+#include "gimppropgui-supernova.h"
+#include "gimppropgui-utils.h"
+#include "gimppropgui-vignette.h"
+
+#include "gimp-intl.h"
+
+
+#define HAS_KEY(p,k,v) gimp_gegl_param_spec_has_key (p, k, v)
+
+
+static gboolean gimp_prop_string_to_boolean (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data);
+static void gimp_prop_config_notify (GObject *config,
+ GParamSpec *pspec,
+ GtkWidget *widget);
+static void gimp_prop_widget_show (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_free_label_ref (GWeakRef *label_ref);
+
+/* public functions */
+
+GtkWidget *
+gimp_prop_widget_new (GObject *config,
+ const gchar *property_name,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator,
+ const gchar **label)
+{
+ GParamSpec *pspec;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
+ property_name);
+
+ return gimp_prop_widget_new_from_pspec (config, pspec, area, context,
+ create_picker_func,
+ create_controller_func,
+ creator,
+ label);
+}
+
+GtkWidget *
+gimp_prop_widget_new_from_pspec (GObject *config,
+ GParamSpec *pspec,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator,
+ const gchar **label)
+{
+ GtkWidget *widget = NULL;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (pspec != NULL, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+ g_return_val_if_fail (label != NULL, NULL);
+
+ *label = NULL;
+
+ if (GEGL_IS_PARAM_SPEC_SEED (pspec))
+ {
+ widget = gimp_prop_random_seed_new (config, pspec->name);
+
+ *label = g_param_spec_get_nick (pspec);
+ }
+ else if (G_IS_PARAM_SPEC_INT (pspec) ||
+ G_IS_PARAM_SPEC_UINT (pspec) ||
+ G_IS_PARAM_SPEC_FLOAT (pspec) ||
+ G_IS_PARAM_SPEC_DOUBLE (pspec))
+ {
+ gdouble lower;
+ gdouble upper;
+ gdouble step;
+ gdouble page;
+ gint digits;
+
+ if (GEGL_IS_PARAM_SPEC_DOUBLE (pspec))
+ {
+ GeglParamSpecDouble *gspec = GEGL_PARAM_SPEC_DOUBLE (pspec);
+
+ lower = gspec->ui_minimum;
+ upper = gspec->ui_maximum;
+ step = gspec->ui_step_small;
+ page = gspec->ui_step_big;
+ digits = gspec->ui_digits;
+ }
+ else if (GEGL_IS_PARAM_SPEC_INT (pspec))
+ {
+ GeglParamSpecInt *gspec = GEGL_PARAM_SPEC_INT (pspec);
+
+ lower = gspec->ui_minimum;
+ upper = gspec->ui_maximum;
+ step = gspec->ui_step_small;
+ page = gspec->ui_step_big;
+ digits = 0;
+ }
+ else
+ {
+ gdouble value;
+
+ /* Get the min and max for the given property. */
+ _gimp_prop_widgets_get_numeric_values (config, pspec,
+ &value, &lower, &upper,
+ G_STRFUNC);
+
+ if ((upper - lower <= 1.0) &&
+ (G_IS_PARAM_SPEC_FLOAT (pspec) ||
+ G_IS_PARAM_SPEC_DOUBLE (pspec)))
+ {
+ step = 0.01;
+ page = 0.1;
+ digits = 4;
+ }
+ else if ((upper - lower <= 10.0) &&
+ (G_IS_PARAM_SPEC_FLOAT (pspec) ||
+ G_IS_PARAM_SPEC_DOUBLE (pspec)))
+ {
+ step = 0.1;
+ page = 1.0;
+ digits = 3;
+ }
+ else
+ {
+ step = 1.0;
+ page = 10.0;
+ digits = (G_IS_PARAM_SPEC_FLOAT (pspec) ||
+ G_IS_PARAM_SPEC_DOUBLE (pspec)) ? 2 : 0;
+ }
+ }
+
+ widget = gimp_prop_spin_scale_new (config, pspec->name, NULL,
+ step, page, digits);
+
+ if (HAS_KEY (pspec, "unit", "degree") &&
+ (upper - lower) == 360.0)
+ {
+ GtkWidget *hbox;
+ GtkWidget *dial;
+
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (widget), TRUE);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ dial = gimp_prop_angle_dial_new (config, pspec->name);
+ g_object_set (dial,
+ "clockwise-angles", HAS_KEY (pspec, "direction", "cw"),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), dial, FALSE, FALSE, 0);
+ gtk_widget_show (dial);
+
+ gimp_help_set_help_data (hbox, g_param_spec_get_blurb (pspec), NULL);
+ gimp_prop_gui_bind_label (hbox, widget);
+
+ widget = hbox;
+ }
+ else if (HAS_KEY (pspec, "unit", "kelvin"))
+ {
+ GtkWidget *hbox;
+ GtkWidget *button;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ button = gimp_prop_kelvin_presets_new (config, pspec->name);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (hbox, g_param_spec_get_blurb (pspec), NULL);
+ gimp_prop_gui_bind_label (hbox, widget);
+
+ widget = hbox;
+ }
+ else
+ {
+ gimp_prop_gui_bind_label (widget, widget);
+
+ if (area &&
+ (HAS_KEY (pspec, "unit", "pixel-coordinate") ||
+ HAS_KEY (pspec, "unit", "pixel-distance")) &&
+ (HAS_KEY (pspec, "axis", "x") ||
+ HAS_KEY (pspec, "axis", "y")))
+ {
+ gdouble min = lower;
+ gdouble max = upper;
+
+ if (HAS_KEY (pspec, "unit", "pixel-coordinate"))
+ {
+ /* limit pixel coordinate scales to the actual area */
+
+ gint off_x = area->x;
+ gint off_y = area->y;
+
+ if (HAS_KEY (pspec, "axis", "x"))
+ {
+ min = MAX (lower, off_x);
+ max = MIN (upper, off_x + area->width);
+ }
+ else if (HAS_KEY (pspec, "axis","y"))
+ {
+ min = MAX (lower, off_y);
+ max = MIN (upper, off_y + area->height);
+ }
+ }
+ else if (HAS_KEY (pspec, "unit", "pixel-distance"))
+ {
+ /* limit pixel distance scales to the same value on the
+ * x and y axes, so linked values have the same range,
+ * we use MAX (width, height), see issue #2540
+ */
+
+ max = MIN (upper, MAX (area->width, area->height));
+ }
+
+ gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (widget),
+ min, max);
+ }
+ }
+ }
+ else if (G_IS_PARAM_SPEC_STRING (pspec))
+ {
+ *label = g_param_spec_get_nick (pspec);
+
+ if (GIMP_IS_PARAM_SPEC_CONFIG_PATH (pspec))
+ {
+ widget =
+ gimp_prop_file_chooser_button_new (config, pspec->name,
+ g_param_spec_get_nick (pspec),
+ GTK_FILE_CHOOSER_ACTION_OPEN);
+ }
+ else if (HAS_KEY (pspec, "multiline", "true"))
+ {
+ GtkTextBuffer *buffer;
+ GtkWidget *view;
+
+ buffer = gimp_prop_text_buffer_new (config, pspec->name, -1);
+ view = gtk_text_view_new_with_buffer (buffer);
+ g_object_unref (buffer);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_size_request (widget, -1, 150);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget),
+ GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (widget), view);
+ gtk_widget_show (view);
+ }
+ else if (HAS_KEY (pspec, "error", "true"))
+ {
+ GtkWidget *l;
+
+ widget = gimp_message_box_new (GIMP_ICON_WILBER_EEK);
+ gimp_message_box_set_primary_text (GIMP_MESSAGE_BOX (widget), "%s",
+ *label);
+ gimp_message_box_set_text (GIMP_MESSAGE_BOX (widget), "%s", "");
+
+ l = GIMP_MESSAGE_BOX (widget)->label[1];
+
+ g_object_bind_property (config, pspec->name,
+ l, "label",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property_full (config, pspec->name,
+ widget, "visible",
+ G_BINDING_SYNC_CREATE,
+ gimp_prop_string_to_boolean,
+ NULL,
+ NULL, NULL);
+ *label = NULL;
+ }
+ else
+ {
+ widget = gimp_prop_entry_new (config, pspec->name, -1);
+ }
+
+ }
+ else if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
+ {
+ widget = gimp_prop_check_button_new (config, pspec->name,
+ g_param_spec_get_nick (pspec));
+
+ gimp_prop_gui_bind_label (widget, widget);
+ }
+ else if (G_IS_PARAM_SPEC_ENUM (pspec))
+ {
+ widget = gimp_prop_enum_combo_box_new (config, pspec->name, 0, 0);
+ gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (widget),
+ g_param_spec_get_nick (pspec));
+
+ gimp_prop_gui_bind_label (widget, widget);
+ }
+ else if (GIMP_IS_PARAM_SPEC_RGB (pspec))
+ {
+ gboolean has_alpha;
+ GtkWidget *button;
+
+ has_alpha = gimp_param_spec_rgb_has_alpha (pspec);
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ button = gimp_prop_color_button_new (config, pspec->name,
+ g_param_spec_get_nick (pspec),
+ 128, 24,
+ has_alpha ?
+ GIMP_COLOR_AREA_SMALL_CHECKS :
+ GIMP_COLOR_AREA_FLAT);
+ gimp_color_button_set_update (GIMP_COLOR_BUTTON (button), TRUE);
+ gimp_color_panel_set_context (GIMP_COLOR_PANEL (button), context);
+ gtk_box_pack_start (GTK_BOX (widget), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_prop_gui_bind_tooltip (button, widget);
+
+ if (create_picker_func)
+ {
+ button = create_picker_func (creator,
+ pspec->name,
+ GIMP_ICON_COLOR_PICKER_GRAY,
+ _("Pick color from the image"),
+ /* pick_abyss = */ FALSE,
+ NULL, NULL);
+ gtk_box_pack_start (GTK_BOX (widget), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+ }
+
+ *label = g_param_spec_get_nick (pspec);
+ }
+ else
+ {
+ g_warning ("%s: not supported: %s (%s)\n", G_STRFUNC,
+ g_type_name (G_TYPE_FROM_INSTANCE (pspec)), pspec->name);
+ }
+
+ /* if we have any keys for dynamic properties, listen to config's notify
+ * signal, and update the properties accordingly.
+ */
+ if (gegl_param_spec_get_property_key (pspec, "sensitive") ||
+ gegl_param_spec_get_property_key (pspec, "visible") ||
+ gegl_param_spec_get_property_key (pspec, "label") ||
+ gegl_param_spec_get_property_key (pspec, "description"))
+ {
+ g_object_set_data (G_OBJECT (widget), "gimp-prop-pspec", pspec);
+
+ g_signal_connect_object (config, "notify",
+ G_CALLBACK (gimp_prop_config_notify),
+ widget, 0);
+
+ if (gegl_param_spec_get_property_key (pspec, "visible"))
+ {
+ /* a bit of a hack: if we have a dynamic "visible" property key,
+ * connect to the widget's "show" signal, so that we can intercept
+ * our caller's gtk_widget_show() call, and keep the widget hidden if
+ * necessary.
+ */
+ g_signal_connect (widget, "show",
+ G_CALLBACK (gimp_prop_widget_show),
+ config);
+ }
+
+ /* update all the properties now */
+ gimp_prop_config_notify (config, NULL, widget);
+ }
+
+ return widget;
+}
+
+
+typedef GtkWidget * (* GimpPropGuiNewFunc) (GObject *config,
+ GParamSpec **param_specs,
+ guint n_param_specs,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator);
+
+static const struct
+{
+ const gchar *config_type;
+ GimpPropGuiNewFunc gui_new_func;
+}
+gui_new_funcs[] =
+{
+ { "GimpColorBalanceConfig",
+ _gimp_prop_gui_new_color_balance },
+ { "GimpHueSaturationConfig",
+ _gimp_prop_gui_new_hue_saturation },
+ { "GimpGegl-gegl-color-rotate-config",
+ _gimp_prop_gui_new_color_rotate },
+ { "GimpGegl-gegl-color-to-alpha-config",
+ _gimp_prop_gui_new_color_to_alpha },
+ { "GimpGegl-gegl-convolution-matrix-config",
+ _gimp_prop_gui_new_convolution_matrix },
+ { "GimpGegl-gegl-channel-mixer-config",
+ _gimp_prop_gui_new_channel_mixer },
+ { "GimpGegl-gegl-diffraction-patterns-config",
+ _gimp_prop_gui_new_diffraction_patterns },
+ { "GimpGegl-gegl-focus-blur-config",
+ _gimp_prop_gui_new_focus_blur },
+ { "GimpGegl-gegl-motion-blur-circular-config",
+ _gimp_prop_gui_new_motion_blur_circular },
+ { "GimpGegl-gegl-motion-blur-linear-config",
+ _gimp_prop_gui_new_motion_blur_linear },
+ { "GimpGegl-gegl-motion-blur-zoom-config",
+ _gimp_prop_gui_new_motion_blur_zoom },
+ { "GimpGegl-gegl-newsprint-config",
+ _gimp_prop_gui_new_newsprint },
+ { "GimpGegl-gegl-panorama-projection-config",
+ _gimp_prop_gui_new_panorama_projection },
+ { "GimpGegl-gegl-recursive-transform-config",
+ _gimp_prop_gui_new_recursive_transform },
+ { "GimpGegl-gegl-shadows-highlights-config",
+ _gimp_prop_gui_new_shadows_highlights },
+ { "GimpGegl-gegl-spiral-config",
+ _gimp_prop_gui_new_spiral },
+ { "GimpGegl-gegl-supernova-config",
+ _gimp_prop_gui_new_supernova },
+ { "GimpGegl-gegl-vignette-config",
+ _gimp_prop_gui_new_vignette },
+ { NULL,
+ _gimp_prop_gui_new_generic }
+};
+
+
+GtkWidget *
+gimp_prop_gui_new (GObject *config,
+ GType owner_type,
+ GParamFlags flags,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker_func,
+ GimpCreateControllerFunc create_controller_func,
+ gpointer creator)
+{
+ GtkWidget *gui = NULL;
+ GParamSpec **param_specs;
+ guint n_param_specs;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ param_specs = gimp_operation_config_list_properties (config,
+ owner_type, flags,
+ &n_param_specs);
+
+ if (param_specs)
+ {
+ const gchar *config_type_name = G_OBJECT_TYPE_NAME (config);
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (gui_new_funcs); i++)
+ {
+ if (! gui_new_funcs[i].config_type ||
+ ! strcmp (gui_new_funcs[i].config_type, config_type_name))
+ {
+ g_printerr ("GUI new func match: %s\n",
+ gui_new_funcs[i].config_type ?
+ gui_new_funcs[i].config_type : "generic fallback");
+
+ gui = gui_new_funcs[i].gui_new_func (config,
+ param_specs, n_param_specs,
+ area,
+ context,
+ create_picker_func,
+ create_controller_func,
+ creator);
+ break;
+ }
+ }
+
+ g_free (param_specs);
+ }
+ else
+ {
+ gui = gtk_label_new (_("This operation has no editable properties"));
+ gimp_label_set_attributes (GTK_LABEL (gui),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_misc_set_padding (GTK_MISC (gui), 0, 4);
+ }
+
+ return gui;
+}
+
+void
+gimp_prop_gui_bind_container (GtkWidget *source,
+ GtkWidget *target)
+{
+ g_object_bind_property (source, "sensitive",
+ target, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (source, "visible",
+ target, "visible",
+ G_BINDING_SYNC_CREATE);
+}
+
+void
+gimp_prop_gui_bind_label (GtkWidget *source,
+ GtkWidget *target)
+{
+ GWeakRef *label_ref;
+ const gchar *label;
+
+ /* we want to update "target"'s "label" property whenever the label
+ * expression associated with "source" is reevaluated, however, "source"
+ * might not itself have a "label" property we can bind to. just keep around
+ * a reference to "target", and update its label manually.
+ */
+ g_return_if_fail (g_object_get_data (G_OBJECT (source),
+ "gimp-prop-label-ref") == NULL);
+
+ label_ref = g_slice_new (GWeakRef);
+
+ g_weak_ref_init (label_ref, target);
+
+ g_object_set_data_full (G_OBJECT (source),
+ "gimp-prop-label-ref", label_ref,
+ (GDestroyNotify) gimp_prop_free_label_ref);
+
+ label = g_object_get_data (G_OBJECT (source), "gimp-prop-label");
+
+ if (label)
+ g_object_set (target, "label", label, NULL);
+
+ /* note that "source" might be its own label widget, in which case there's no
+ * need to bind the rest of the properties.
+ */
+ if (source != target)
+ gimp_prop_gui_bind_tooltip (source, target);
+}
+
+void
+gimp_prop_gui_bind_tooltip (GtkWidget *source,
+ GtkWidget *target)
+{
+ g_object_bind_property (source, "tooltip-text",
+ target, "tooltip-text",
+ G_BINDING_SYNC_CREATE);
+}
+
+
+/* private functions */
+
+static gboolean
+gimp_prop_string_to_boolean (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ const gchar *string = g_value_get_string (from_value);
+
+ g_value_set_boolean (to_value, string && *string);
+
+ return TRUE;
+}
+
+static void
+gimp_prop_config_notify (GObject *config,
+ GParamSpec *pspec,
+ GtkWidget *widget)
+{
+ GParamSpec *widget_pspec;
+ GWeakRef *label_ref;
+ GtkWidget *label_widget;
+ gboolean sensitive;
+ gboolean visible;
+ gchar *label;
+ gchar *description;
+
+ widget_pspec = g_object_get_data (G_OBJECT (widget), "gimp-prop-pspec");
+ label_ref = g_object_get_data (G_OBJECT (widget), "gimp-prop-label-ref");
+
+ if (label_ref)
+ label_widget = g_weak_ref_get (label_ref);
+ else
+ label_widget = NULL;
+
+ sensitive = gimp_prop_eval_boolean (config, widget_pspec, "sensitive", TRUE);
+ visible = gimp_prop_eval_boolean (config, widget_pspec, "visible", TRUE);
+ label = gimp_prop_eval_string (config, widget_pspec, "label",
+ g_param_spec_get_nick (widget_pspec));
+ description = gimp_prop_eval_string (config, widget_pspec, "description",
+ g_param_spec_get_blurb (widget_pspec));
+
+ /* we store the label in (and pass ownership over it to) the widget's
+ * "gimp-prop-label" key, so that we can use it to initialize the label
+ * widget's label in gimp_prop_gui_bind_label() upon binding.
+ */
+ g_object_set_data_full (G_OBJECT (widget), "gimp-prop-label", label, g_free);
+
+ g_signal_handlers_block_by_func (widget,
+ gimp_prop_widget_show, config);
+
+ gtk_widget_set_sensitive (widget, sensitive);
+ gtk_widget_set_visible (widget, visible);
+ if (label_widget) g_object_set (label_widget, "label", label, NULL);
+ gimp_help_set_help_data (widget, description, NULL);
+
+ g_signal_handlers_unblock_by_func (widget,
+ gimp_prop_widget_show, config);
+
+ g_free (description);
+
+ if (label_widget)
+ g_object_unref (label_widget);
+}
+
+static void
+gimp_prop_widget_show (GtkWidget *widget,
+ GObject *config)
+{
+ GParamSpec *widget_pspec;
+ gboolean visible;
+
+ widget_pspec = g_object_get_data (G_OBJECT (widget), "gimp-prop-pspec");
+
+ visible = gimp_prop_eval_boolean (config, widget_pspec, "visible", TRUE);
+
+ gtk_widget_set_visible (widget, visible);
+}
+
+static void
+gimp_prop_free_label_ref (GWeakRef *label_ref)
+{
+ g_weak_ref_clear (label_ref);
+
+ g_slice_free (GWeakRef, label_ref);
+}
diff --git a/app/propgui/gimppropgui.h b/app/propgui/gimppropgui.h
new file mode 100644
index 0000000..d5d6bac
--- /dev/null
+++ b/app/propgui/gimppropgui.h
@@ -0,0 +1,60 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppropgui.h
+ * Copyright (C) 2002-2017 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_PROP_GUI_H__
+#define __GIMP_PROP_GUI_H__
+
+
+/* A view on all of an object's properties */
+
+GtkWidget * gimp_prop_widget_new (GObject *config,
+ const gchar *property_name,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker,
+ GimpCreateControllerFunc create_controller,
+ gpointer creator,
+ const gchar **label);
+GtkWidget * gimp_prop_widget_new_from_pspec (GObject *config,
+ GParamSpec *pspec,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker,
+ GimpCreateControllerFunc create_controller,
+ gpointer creator,
+ const gchar **label);
+GtkWidget * gimp_prop_gui_new (GObject *config,
+ GType owner_type,
+ GParamFlags flags,
+ GeglRectangle *area,
+ GimpContext *context,
+ GimpCreatePickerFunc create_picker,
+ GimpCreateControllerFunc create_controller,
+ gpointer creator);
+
+void gimp_prop_gui_bind_container (GtkWidget *source,
+ GtkWidget *target);
+void gimp_prop_gui_bind_label (GtkWidget *source,
+ GtkWidget *target);
+void gimp_prop_gui_bind_tooltip (GtkWidget *source,
+ GtkWidget *target);
+
+
+#endif /* __GIMP_PROP_GUI_H__ */
diff --git a/app/propgui/propgui-types.h b/app/propgui/propgui-types.h
new file mode 100644
index 0000000..11bbc06
--- /dev/null
+++ b/app/propgui/propgui-types.h
@@ -0,0 +1,145 @@
+/* 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 __PROPGUI_TYPES_H__
+#define __PROPGUI_TYPES_H__
+
+
+#include "display/display-enums.h"
+
+#include "widgets/widgets-types.h"
+
+
+/* enums, move to propgui-enums.h if we get more */
+
+typedef enum
+{
+ GIMP_CONTROLLER_TYPE_LINE,
+ GIMP_CONTROLLER_TYPE_SLIDER_LINE,
+ GIMP_CONTROLLER_TYPE_TRANSFORM_GRID,
+ GIMP_CONTROLLER_TYPE_TRANSFORM_GRIDS,
+ GIMP_CONTROLLER_TYPE_GYROSCOPE,
+ GIMP_CONTROLLER_TYPE_FOCUS
+} GimpControllerType;
+
+
+/* structs */
+
+typedef struct
+{
+ gdouble value; /* slider value */
+ gdouble min; /* minimal allowable slider value */
+ gdouble max; /* maximal allowable slider value */
+
+ gboolean visible : 1; /* slider is visible */
+ gboolean selectable : 1; /* slider is selectable */
+ gboolean movable : 1; /* slider movable */
+ gboolean removable : 1; /* slider is removable */
+
+ gboolean autohide : 1; /* whether to autohide the slider */
+ GimpHandleType type; /* slider handle type */
+ gdouble size; /* slider handle size, as a fraction of *
+ * the default size */
+
+ gpointer data; /* user data */
+} GimpControllerSlider;
+
+#define GIMP_CONTROLLER_SLIDER_DEFAULT \
+ ((const GimpControllerSlider) { \
+ .value = 0.0, \
+ .min = 0.0, \
+ .max = 1.0, \
+ \
+ .visible = TRUE, \
+ .selectable = TRUE, \
+ .movable = TRUE, \
+ .removable = FALSE, \
+ \
+ .autohide = FALSE, \
+ .type = GIMP_HANDLE_FILLED_DIAMOND, \
+ .size = 1.0, \
+ \
+ .data = NULL \
+ })
+
+
+/* function types */
+
+typedef void (* GimpPickerCallback) (gpointer data,
+ gpointer identifier,
+ gdouble x,
+ gdouble y,
+ const Babl *sample_format,
+ const GimpRGB *color);
+
+typedef void (* GimpControllerLineCallback) (gpointer data,
+ GeglRectangle *area,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2);
+typedef void (* GimpControllerSliderLineCallback) (gpointer data,
+ GeglRectangle *area,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ const GimpControllerSlider *sliders,
+ gint n_sliders);
+typedef void (* GimpControllerTransformGridCallback) (gpointer data,
+ GeglRectangle *area,
+ const GimpMatrix3 *transform);
+typedef void (* GimpControllerTransformGridsCallback) (gpointer data,
+ GeglRectangle *area,
+ const GimpMatrix3 *transforms,
+ gint n_transforms);
+typedef void (* GimpControllerGyroscopeCallback) (gpointer data,
+ GeglRectangle *area,
+ gdouble yaw,
+ gdouble pitch,
+ gdouble roll,
+ gdouble zoom,
+ gboolean invert);
+typedef void (* GimpControllerFocusCallback) (gpointer data,
+ GeglRectangle *area,
+ GimpLimitType type,
+ gdouble x,
+ gdouble y,
+ gdouble radius,
+ gdouble aspect_ratio,
+ gdouble angle,
+ gdouble inner_limit,
+ gdouble midpoint);
+
+
+typedef GtkWidget * (* GimpCreatePickerFunc) (gpointer creator,
+ const gchar *property_name,
+ const gchar *icon_name,
+ const gchar *tooltip,
+ gboolean pick_abyss,
+ GimpPickerCallback callback,
+ gpointer callback_data);
+
+typedef GCallback (* GimpCreateControllerFunc) (gpointer creator,
+ GimpControllerType controller_type,
+ const gchar *status_title,
+ GCallback callback,
+ gpointer callback_data,
+ gpointer *set_func_data);
+
+
+#endif /* __PROPGUI_TYPES_H__ */