[gnome-builder] gstyle: new lib to handle colors
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] gstyle: new lib to handle colors
- Date: Mon, 18 Jul 2016 00:21:03 +0000 (UTC)
commit ab5b98b623cd015ccf377b8b406cfcdf3983c736
Author: Sebastien Lafargue <slafargue gnome org>
Date: Wed Jul 13 21:30:12 2016 +0200
gstyle: new lib to handle colors
- a color strings lexer/parser
- L*a*b, XYZ, HSV, RGB colorspaces support
- a color object
- a palette object
- a color scale widget
- a color plane widget
- a eyedropper widget
- a color swatch widget
- a color palette widget
- a color panel widget
- a slidein panel widget
- a specific revealer for the color panel
- some assets for the widgets
configure.ac | 7 +
contrib/Makefile.am | 1 +
contrib/gstyle/Makefile.am | 186 ++
contrib/gstyle/OVERVIEW.md | 4 +
contrib/gstyle/TODO.md | 60 +
contrib/gstyle/assets/assets-symbolic.svg | 201 ++
contrib/gstyle/assets/assets-symbolic.txt | 3 +
contrib/gstyle/assets/assets.svg | 1109 ++++++++
contrib/gstyle/assets/assets.txt | 15 +
.../color-scale-slider-horizontal-active.png | Bin 0 -> 1134 bytes
.../color-scale-slider-horizontal-active 2 png | Bin 0 -> 1933 bytes
...cale-slider-horizontal-backdrop-insensitive.png | Bin 0 -> 675 bytes
...le-slider-horizontal-backdrop-insensitive 2 png | Bin 0 -> 905 bytes
.../color-scale-slider-horizontal-backdrop.png | Bin 0 -> 678 bytes
.../color-scale-slider-horizontal-backdrop 2 png | Bin 0 -> 909 bytes
.../assets/color-scale-slider-horizontal-hover.png | Bin 0 -> 1040 bytes
.../color-scale-slider-horizontal-hover 2 png | Bin 0 -> 1838 bytes
.../color-scale-slider-horizontal-insensitive.png | Bin 0 -> 675 bytes
...color-scale-slider-horizontal-insensitive 2 png | Bin 0 -> 905 bytes
.../assets/color-scale-slider-horizontal.png | Bin 0 -> 1022 bytes
.../assets/color-scale-slider-horizontal 2 png | Bin 0 -> 1900 bytes
.../assets/color-scale-slider-vertical-active.png | Bin 0 -> 820 bytes
.../color-scale-slider-vertical-active 2 png | Bin 0 -> 1577 bytes
...-scale-slider-vertical-backdrop-insensitive.png | Bin 0 -> 548 bytes
...cale-slider-vertical-backdrop-insensitive 2 png | Bin 0 -> 878 bytes
.../color-scale-slider-vertical-backdrop.png | Bin 0 -> 533 bytes
.../color-scale-slider-vertical-backdrop 2 png | Bin 0 -> 881 bytes
.../assets/color-scale-slider-vertical-hover.png | Bin 0 -> 796 bytes
.../assets/color-scale-slider-vertical-hover 2 png | Bin 0 -> 1483 bytes
.../color-scale-slider-vertical-insensitive.png | Bin 0 -> 548 bytes
.../color-scale-slider-vertical-insensitive 2 png | Bin 0 -> 878 bytes
.../gstyle/assets/color-scale-slider-vertical.png | Bin 0 -> 779 bytes
.../assets/color-scale-slider-vertical 2 png | Bin 0 -> 1500 bytes
contrib/gstyle/assets/eyedropper-symbolic.svg | 111 +
contrib/gstyle/assets/render-assets-symbolic.py | 50 +
contrib/gstyle/assets/render-assets.sh | 34 +
contrib/gstyle/assets/unit-degree-symbolic.svg | 96 +
contrib/gstyle/assets/unit-percent-symbolic.svg | 106 +
contrib/gstyle/check-palette.sh | 17 +
contrib/gstyle/colorlexer-regen.sh | 38 +
contrib/gstyle/data/palettes/basic.xml | 32 +
contrib/gstyle/data/palettes/palette.rng | 134 +
contrib/gstyle/gstyle-animation.c | 51 +
contrib/gstyle/gstyle-animation.h | 31 +
contrib/gstyle/gstyle-cielab.c | 64 +
contrib/gstyle/gstyle-cielab.h | 45 +
contrib/gstyle/gstyle-color-component.c | 50 +
contrib/gstyle/gstyle-color-component.h | 45 +
contrib/gstyle/gstyle-color-convert.c | 843 ++++++
contrib/gstyle/gstyle-color-convert.h | 75 +
contrib/gstyle/gstyle-color-filter.c | 42 +
contrib/gstyle/gstyle-color-filter.h | 36 +
contrib/gstyle/gstyle-color-item.c | 209 ++
contrib/gstyle/gstyle-color-item.h | 58 +
contrib/gstyle/gstyle-color-panel-actions.c | 84 +
contrib/gstyle/gstyle-color-panel-actions.h | 32 +
contrib/gstyle/gstyle-color-panel-private.h | 151 +
contrib/gstyle/gstyle-color-panel.c | 1465 ++++++++++
contrib/gstyle/gstyle-color-panel.h | 79 +
contrib/gstyle/gstyle-color-plane.c | 1648 +++++++++++
contrib/gstyle/gstyle-color-plane.h | 89 +
contrib/gstyle/gstyle-color-predefined.h | 189 ++
contrib/gstyle/gstyle-color-scale.c | 738 +++++
contrib/gstyle/gstyle-color-scale.h | 78 +
contrib/gstyle/gstyle-color-widget.c | 1252 ++++++++
contrib/gstyle/gstyle-color-widget.h | 71 +
contrib/gstyle/gstyle-color.c | 1305 +++++++++
contrib/gstyle/gstyle-color.h | 110 +
contrib/gstyle/gstyle-colorlexer.c | 3002 ++++++++++++++++++++
contrib/gstyle/gstyle-colorlexer.h | 48 +
contrib/gstyle/gstyle-colorlexer.re | 142 +
contrib/gstyle/gstyle-css-provider.c | 158 +
contrib/gstyle/gstyle-css-provider.h | 37 +
contrib/gstyle/gstyle-eyedropper.c | 400 +++
contrib/gstyle/gstyle-eyedropper.h | 35 +
contrib/gstyle/gstyle-hsv.c | 64 +
contrib/gstyle/gstyle-hsv.h | 45 +
contrib/gstyle/gstyle-palette-widget.c | 1454 ++++++++++
contrib/gstyle/gstyle-palette-widget.h | 83 +
contrib/gstyle/gstyle-palette.c | 1134 ++++++++
contrib/gstyle/gstyle-palette.h | 86 +
contrib/gstyle/gstyle-private.h | 31 +
contrib/gstyle/gstyle-revealer.c | 341 +++
contrib/gstyle/gstyle-revealer.h | 37 +
contrib/gstyle/gstyle-slidein.c | 1328 +++++++++
contrib/gstyle/gstyle-slidein.h | 64 +
contrib/gstyle/gstyle-types.h | 34 +
contrib/gstyle/gstyle-utils.c | 172 ++
contrib/gstyle/gstyle-utils.h | 93 +
contrib/gstyle/gstyle-xyz.c | 64 +
contrib/gstyle/gstyle-xyz.h | 45 +
contrib/gstyle/gstyle.gresource.xml | 40 +
contrib/gstyle/tests/Makefile.am | 94 +
contrib/gstyle/tests/data/gstyle-color-editor.ui | 280 ++
contrib/gstyle/tests/data/palette.gpl | 142 +
contrib/gstyle/tests/data/palette.xml | 32 +
contrib/gstyle/tests/test-gstyle-color-panel.c | 78 +
contrib/gstyle/tests/test-gstyle-color-plane.c | 142 +
contrib/gstyle/tests/test-gstyle-color-scale.c | 70 +
contrib/gstyle/tests/test-gstyle-color-widget.c | 85 +
contrib/gstyle/tests/test-gstyle-color.c | 397 +++
contrib/gstyle/tests/test-gstyle-palette-widget.c | 76 +
contrib/gstyle/tests/test-gstyle-palette.c | 74 +
contrib/gstyle/tests/test-gstyle-parse.c | 152 +
contrib/gstyle/theme/gstyle.css | 120 +
contrib/gstyle/ui/gstyle-color-panel.ui | 1011 +++++++
contrib/gstyle/ui/gstyle-palette-widget.ui | 106 +
po/POTFILES.in | 4 +
108 files changed, 22844 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a4a02a1..94b4e56 100644
--- a/configure.ac
+++ b/configure.ac
@@ -198,6 +198,9 @@ PKG_CHECK_MODULES(EGG, [glib-2.0 >= glib_required_version
PKG_CHECK_MODULES(GD, [gtk+-3.0 >= gtk_required_version])
PKG_CHECK_MODULES(GEDIT, [glib-2.0 >= glib_required_version
gtk+-3.0 >= gtk_required_version])
+PKG_CHECK_MODULES(GSTYLE, [glib-2.0 >= glib_required_version
+ gtk+-3.0 >= gtk_required_version
+ gtksourceview-3.0 >= gtksourceview_required_version])
PKG_CHECK_MODULES(ICONS, [gio-2.0 >= glib_required_version])
PKG_CHECK_MODULES(LIBIDE, [gio-2.0 >= glib_required_version
gio-unix-2.0 >= glib_required_version
@@ -443,6 +446,7 @@ PLUGIN_CFLAGS="$PLUGIN_CFLAGS -I\$(top_builddir)/contrib/pnl"
PLUGIN_CFLAGS="$PLUGIN_CFLAGS -I\$(top_builddir)/libide"
PLUGIN_CFLAGS="$PLUGIN_CFLAGS -I\$(top_srcdir)/contrib/egg"
PLUGIN_CFLAGS="$PLUGIN_CFLAGS -I\$(top_srcdir)/contrib/gd"
+PLUGIN_CFLAGS="$PLUGIN_CFLAGS -I\$(top_srcdir)/contrib/gstyle"
PLUGIN_CFLAGS="$PLUGIN_CFLAGS -I\$(top_srcdir)/contrib/nautilus"
PLUGIN_CFLAGS="$PLUGIN_CFLAGS -I\$(top_srcdir)/contrib/pnl"
PLUGIN_CFLAGS="$PLUGIN_CFLAGS -I\$(top_srcdir)/contrib/rg"
@@ -464,6 +468,7 @@ PLUGIN_VALAFLAGS="$PLUGIN_VALAFLAGS --target-glib=2.44"
PLUGIN_VALAFLAGS="$PLUGIN_VALAFLAGS --thread"
PLUGIN_VALAFLAGS="$PLUGIN_VALAFLAGS --vapidir \$(top_builddir)/libide"
PLUGIN_VALAFLAGS="$PLUGIN_VALAFLAGS --vapidir \$(top_builddir)/contrib/egg"
+PLUGIN_VALAFLAGS="$PLUGIN_VALAFLAGS --vapidir \$(top_builddir)/contrib/gstyle"
PLUGIN_VALAFLAGS="$PLUGIN_VALAFLAGS --vapidir \$(top_builddir)/contrib/pnl"
PLUGIN_VALAFLAGS="$PLUGIN_VALAFLAGS --vapidir \$(top_builddir)/contrib/tmpl"
PLUGIN_VALAFLAGS="$PLUGIN_VALAFLAGS --pkg libide-1.0"
@@ -483,6 +488,8 @@ AC_CONFIG_FILES([
contrib/Makefile
contrib/egg/Makefile
contrib/gd/Makefile
+ contrib/gstyle/Makefile
+ contrib/gstyle/tests/Makefile
contrib/libeditorconfig/Makefile
contrib/nautilus/Makefile
contrib/pnl/Makefile
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
index 516886b..b7169b0 100644
--- a/contrib/Makefile.am
+++ b/contrib/Makefile.am
@@ -6,6 +6,7 @@ SUBDIRS = \
pnl \
rg \
search \
+ gstyle \
tmpl \
xml \
$(NULL)
diff --git a/contrib/gstyle/Makefile.am b/contrib/gstyle/Makefile.am
new file mode 100644
index 0000000..ba9f636
--- /dev/null
+++ b/contrib/gstyle/Makefile.am
@@ -0,0 +1,186 @@
+CLEANFILES =
+DISTCLEANFILES =
+EXTRA_DIST =
+BUILT_SOURCES =
+
+SUBDIRS = . tests
+
+pkglibdir = $(libdir)/gnome-builder
+pkglib_LTLIBRARIES = libgstyle-private.la
+
+headersdir = $(includedir)/gnome-builder-@VERSION@/gstyle
+headers_DATA = \
+ gstyle-animation.h \
+ gstyle-cielab.h \
+ gstyle-color.h \
+ gstyle-color-component.h \
+ gstyle-color-convert.h \
+ gstyle-color-filter.h \
+ gstyle-color-item.h \
+ gstyle-color-panel.h \
+ gstyle-color-panel-private.h \
+ gstyle-color-panel-actions.h \
+ gstyle-color-plane.h \
+ gstyle-color-predefined.h \
+ gstyle-color-scale.h \
+ gstyle-color-widget.h \
+ gstyle-colorlexer.h \
+ gstyle-css-provider.h \
+ gstyle-eyedropper.h \
+ gstyle-hsv.h \
+ gstyle-palette.h \
+ gstyle-palette-widget.h \
+ gstyle-private.h \
+ gstyle-revealer.c \
+ gstyle-slidein.h \
+ gstyle-types.h \
+ gstyle-utils.h \
+ gstyle-xyz.h \
+ $(NULL)
+
+libgstyle_private_la_SOURCES = \
+ $(headers_DATA) \
+ gstyle-animation.c \
+ gstyle-cielab.c \
+ gstyle-color.c \
+ gstyle-color-component.c \
+ gstyle-color-convert.c \
+ gstyle-color-filter.c \
+ gstyle-color-item.c \
+ gstyle-color-panel.c \
+ gstyle-color-panel-actions.c \
+ gstyle-color-plane.c \
+ gstyle-color-scale.c \
+ gstyle-color-widget.c \
+ gstyle-colorlexer.c \
+ gstyle-css-provider.c \
+ gstyle-eyedropper.c \
+ gstyle-hsv.c \
+ gstyle-palette.c \
+ gstyle-palette-widget.c \
+ gstyle-revealer.h \
+ gstyle-slidein.c \
+ gstyle-utils.c \
+ gstyle-xyz.c \
+ $(NULL)
+
+libgstyle_private_la_CFLAGS = \
+ $(GSTYLE_CFLAGS) \
+ $(XML_CFLAGS) \
+ $(DEBUG_CFLAGS) \
+ $(OPTIMIZE_CFLAGS) \
+ -I$(top_srcdir)/contrib/search \
+ -I$(top_builddir)/contrib/search \
+ $(NULL)
+
+libgstyle_private_la_LIBADD = \
+ $(GSTYLE_LIBS) \
+ $(XML_LIBS) \
+ $(top_builddir)/contrib/search/libsearch.la \
+ $(NULL)
+
+libgstyle_private_la_LDFLAGS = \
+ $(OPTIMIZE_LDFLAGS) \
+ --no-undefined \
+ $(NULL)
+
+nodist_libgstyle_private_la_SOURCES = \
+ gstyle-resources.c \
+ gstyle-resources.h \
+ $(NULL)
+
+glib_resources_h = gstyle-resources.h
+glib_resources_c = gstyle-resources.c
+glib_resources_xml = gstyle.gresource.xml
+glib_resources_namespace = gstyle
+include $(top_srcdir)/build/autotools/Makefile.am.gresources
+
+
+if HAVE_INTROSPECTION
+-include $(INTROSPECTION_MAKEFILE)
+
+INTROSPECTION_GIRS =
+INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) --warn-all
+INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
+
+introspection_sources = \
+ gstyle-cielab.h \
+ gstyle-cielab.c \
+ gstyle-color.h \
+ gstyle-color.c \
+ gstyle-color-component.h \
+ gstyle-color-component.c \
+ gstyle-color-convert.h \
+ gstyle-color-convert.c \
+ gstyle-color-filter.h \
+ gstyle-color-filter.c \
+ gstyle-color-item.h \
+ gstyle-color-item.c \
+ gstyle-color-panel.h \
+ gstyle-color-panel.c \
+ gstyle-color-plane.h \
+ gstyle-color-plane.c \
+ gstyle-color-scale.h \
+ gstyle-color-scale.c \
+ gstyle-color-widget.h \
+ gstyle-color-widget.c \
+ gstyle-eyedropper.h \
+ gstyle-eyedropper.c \
+ gstyle-hsv.h \
+ gstyle-hsv.c \
+ gstyle-palette.h \
+ gstyle-palette.c \
+ gstyle-palette-widget.h \
+ gstyle-palette-widget.c \
+ gstyle-slidein.h \
+ gstyle-slidein.c \
+ gstyle-xyz.h \
+ gstyle-xyz.c \
+ $(NULL)
+
+Gstyle-1.0.gir: libgstyle-private.la
+Gstyle_1_0_gir_INCLUDES = Gio-2.0 Gdk-3.0 Gtk-3.0 GtkSource-3.0
+Gstyle_1_0_gir_CFLAGS = $(libgstyle_private_la_CFLAGS)
+Gstyle_1_0_gir_LIBS = libgstyle-private.la
+Gstyle_1_0_gir_FILES = $(introspection_sources)
+Gstyle_1_0_gir_SCANNERFLAGS = \
+ --c-include="gstyle-private.h" \
+ -n Gstyle \
+ --identifier-prefix Gstyle \
+ --symbol-prefix gstyle \
+ $(NULL)
+INTROSPECTION_GIRS += Gstyle-1.0.gir
+
+girdir = $(datadir)/gnome-builder/gir-1.0
+dist_gir_DATA = $(INTROSPECTION_GIRS)
+
+typelibdir = $(pkglibdir)/girepository-1.0
+typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
+
+CLEANFILES += $(dist_gir_DATA) $(typelib_DATA)
+endif
+
+
+if ENABLE_VAPIGEN
+-include $(VAPIGEN_MAKEFILE)
+
+gstyle-private.vapi: Gstyle-1.0.gir
+
+VAPIGEN_VAPIS = gstyle-private.vapi
+
+gstyle_private_vapi_DEPS = gio-2.0 gtk+-3.0 GtkSource-3.0
+gstyle_private_vapi_METADATADIRS = $(srcdir)
+gstyle_private_vapi_FILES = Gstyle-1.0.gir
+
+gstyle-private.deps: Makefile
+ $(AM_V_GEN) echo $(libgstyle_private_vapi_DEPS) | tr ' ' '\n' > $@
+
+vapidir = $(datadir)/gnome-builder/vapi
+vapi_DATA = $(VAPIGEN_VAPIS) $(VAPIGEN_VAPIS:.vapi=.deps)
+
+EXTRA_DIST += gstyle-private.deps
+
+DISTCLEANFILES += $(vapi_DATA)
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/contrib/gstyle/OVERVIEW.md b/contrib/gstyle/OVERVIEW.md
new file mode 100644
index 0000000..618d044
--- /dev/null
+++ b/contrib/gstyle/OVERVIEW.md
@@ -0,0 +1,4 @@
+# Dependencies :
+
+In the current state, GStyle is dependent of a few Gnome Builder contrib parts:
+ - fuzzy.h is used by the search color string entry.
diff --git a/contrib/gstyle/TODO.md b/contrib/gstyle/TODO.md
new file mode 100644
index 0000000..abd7480
--- /dev/null
+++ b/contrib/gstyle/TODO.md
@@ -0,0 +1,60 @@
+#TODO list :
+
+## Enhancements :
+
+### Color :
+- add HSL, HWB and CMYK.
+- add illuminant management.
+
+### Color widget :
+- fix border-radius calculation, result different than the one used by gtk+.
+
+### Color plane :
+- add circle cursor.
+- icon for out-of-gamut color.
+- use filtering framework (reduce set of colors like websafe and colorblindness amongst others)
+
+### Panel :
+- add a horizontal displayed mode ?
+- add a mini-picker mode to be displayed in a popover at cursor position.
+
+### Palettes :
+- add more load/save formats :
+ - including the gtksourceview style scheme : filter the unneeded part but keep it for save it again.
+- allow scroll when drag at the end of list (both list or flow mode).
+
+### CSS parser :
+- add color functions parsing:
+ - darker, lighter, mix, alpha.
+
+### Sort :
+- sort by various mode:
+ - by name.
+ - by light.
+ - by hue.
+
+- sort relative to the selected color:
+ - by approching color deltaE.2000 (calculation already in gstyle)
+
+ Do sorting change the saved order of colors or just the view ?
+
+### Drag'n drop :
+- dnd-lock: a lock-symbolic lock icon.
+
+### Slidein :
+- add optional right click to close the slidein (property).
+- is it possible to not close the slidein when dragging the paned border to extend the panel ?
+
+### Color plane :
+- fix cursor visibility on border when using the sliders.
+
+### Tests :
+- make them real test, not just result output.
+- fix panel test to use prefs pages.
+- fix palette widget test to be able to select a palette.
+
+## FIXES :
+
+### Theme :
+- add a dark theme.
+- add backdrop state.
diff --git a/contrib/gstyle/assets/assets-symbolic.svg b/contrib/gstyle/assets/assets-symbolic.svg
new file mode 100644
index 0000000..fbf46b6
--- /dev/null
+++ b/contrib/gstyle/assets/assets-symbolic.svg
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448"
+ height="1052.3622"
+ viewBox="0 0 744.09449 1052.3622"
+ id="svg4370"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="assets-symbolic.svg">
+ <defs
+ id="defs4372">
+ <inkscape:path-effect
+ effect="spiro"
+ id="path-effect4178"
+ is_visible="true" />
+ <linearGradient
+ id="linearGradient4418">
+ <stop
+ id="stop4420"
+ offset="0"
+ style="stop-color:#f4f4f4;stop-opacity:1" />
+ <stop
+ style="stop-color:#f4f4f4;stop-opacity:1"
+ offset="0.2"
+ id="stop4422" />
+ <stop
+ id="stop4424"
+ offset="1"
+ style="stop-color:#bce8e7;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="killme">
+ <stop
+ id="stop5583-0-92-8-0-7-6"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ style="stop-color:#ededed;stop-opacity:1"
+ offset="0.40000001"
+ id="stop5585-4-7-2-7-9-9" />
+ <stop
+ id="stop5587-6-7-2-0-3-1"
+ offset="1"
+ style="stop-color:#41d3d3;stop-opacity:1" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#3a3b39"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="32"
+ inkscape:cx="342.95481"
+ inkscape:cy="870.42534"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1016"
+ inkscape:window-x="1920"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ units="px"
+ showborder="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:object-nodes="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4918"
+ empcolor="#3ff5ff"
+ empopacity="0.56862745"
+ color="#36ebff"
+ opacity="0.32941176"
+ dotted="false" />
+ <sodipodi:guide
+ position="304.55984,863.01144"
+ orientation="1,0"
+ id="guide4174" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4375">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="unit-percent-symbolic"
+ inkscape:label="#g4167"
+ transform="translate(0.12253463,0)">
+ <circle
+ style="fill:none;fill-rule:evenodd;stroke:#bebebe;stroke-width:0.89999998"
+ id="circle7700"
+ cx="340.18726"
+ cy="160.54944"
+ r="2.7372432" />
+ <ellipse
+ cy="168.16022"
+ cx="344.80249"
+ id="ellipse7702"
+ style="fill:none;fill-rule:evenodd;stroke:#bebebe;stroke-width:0.90353149"
+ rx="2.74575"
+ ry="2.7502201" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#bebebe;stroke-width:0.71361196;stroke-linecap:round"
+ d="m 338,170.3622 9,-12"
+ id="path7704"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <rect
+ y="156.3622"
+ x="336"
+ height="16"
+ width="13"
+ id="rect9598"
+ style="fill:none;fill-opacity:0.2118644;fill-rule:evenodd;stroke-width:0;stroke-linecap:round" />
+ </g>
+ <g
+ transform="translate(696,388.00002)"
+ id="unit-degree-symbolic"
+ inkscape:label="#g9611">
+ <circle
+ r="2.7372432"
+ cy="-227.45058"
+ cx="-335.81274"
+ id="circle7708"
+ style="fill:none;fill-rule:evenodd;stroke:#bebebe;stroke-width:0.89999998" />
+ <rect
+ y="-231.63782"
+ x="-340"
+ height="16"
+ width="13"
+ id="rect9609"
+ style="fill:none;fill-opacity:0.2118644;fill-rule:evenodd;stroke-width:0;stroke-linecap:round" />
+ </g>
+ <g
+ id="eyedropper-symbolic"
+ transform="translate(20.625,-0.6875)"
+ inkscape:label="#g4291">
+ <path
+ sodipodi:nodetypes="ccc"
+ inkscape:connector-curvature="0"
+ id="path4190"
+ d="m 325.08221,181.00746 -9.39652,9.37044 c -0.0593,0.19104 -0.13503,0.70852 -0.31608,1.83278"
+
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.85961121;stroke-linecap:round;stroke-linejoin:round"
/>
+ <path
+ d="m 330.01187,178.39722 c 0.50034,0.49896 0.72124,1.67539 0.18776,2.20739 l -1.42563,1.42167 m
1.23974,-3.62719 c -0.50034,-0.49896 -1.67528,-0.7145 -2.20877,-0.18249 l -1.42563,1.42168"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.85961121"
+ id="path4194"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path4196"
+ style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.36000001"
+ d="m 327.65392,180.74863 1.99551,2.00859 c 0.74951,0.75441 -0.0403,1.57988 -0.81777,0.81834 l
-1.96434,-1.93901 m 0.79322,-0.88131 -2.00702,-1.95616 c -0.76156,-0.74225 -1.61765,0.0175 -0.854,0.79288 l
2.0678,2.04459"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cscccscc" />
+ <path
+
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.85961121;stroke-linecap:round;stroke-linejoin:round"
+ d="m 327.40101,183.31981 -9.39654,9.37046 c -0.2369,0.13014 -0.79628,0.1733 -1.81098,0.35543"
+ id="path4198"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc" />
+ <path
+ sodipodi:nodetypes="cccccc"
+ inkscape:connector-curvature="0"
+ id="path4210"
+ d="m 316.6416,191.77213 0.0909,-0.72459 c 1.08126,-1.05238 2.4727,-2.56936 3.19187,-3.26065 l
1.33643,-6e-5 -3.91848,3.9076 z"
+
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.05546677;stroke-linecap:square;stroke-linejoin:round"
/>
+ </g>
+ </g>
+</svg>
diff --git a/contrib/gstyle/assets/assets-symbolic.txt b/contrib/gstyle/assets/assets-symbolic.txt
new file mode 100644
index 0000000..a648724
--- /dev/null
+++ b/contrib/gstyle/assets/assets-symbolic.txt
@@ -0,0 +1,3 @@
+eyedropper-symbolic
+unit-percent-symbolic
+unit-degree-symbolic
diff --git a/contrib/gstyle/assets/assets.svg b/contrib/gstyle/assets/assets.svg
new file mode 100644
index 0000000..aee0824
--- /dev/null
+++ b/contrib/gstyle/assets/assets.svg
@@ -0,0 +1,1109 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1000"
+ height="1052.3622"
+ id="svg9892"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="assets.svg">
+ <defs
+ id="defs9894">
+ <linearGradient
+ id="linearGradient4418">
+ <stop
+ id="stop4420"
+ offset="0"
+ style="stop-color:#f4f4f4;stop-opacity:1" />
+ <stop
+ style="stop-color:#f4f4f4;stop-opacity:1"
+ offset="0.2"
+ id="stop4422" />
+ <stop
+ id="stop4424"
+ offset="1"
+ style="stop-color:#bce8e7;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4402">
+ <stop
+ id="stop4404"
+ offset="0"
+ style="stop-color:#f4f4f4;stop-opacity:1" />
+ <stop
+ style="stop-color:#f4f4f4;stop-opacity:1"
+ offset="0.57427853"
+ id="stop4406" />
+ <stop
+ id="stop4408"
+ offset="1"
+ style="stop-color:#e8e8e7;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="dark-normal">
+ <stop
+ style="stop-color:#323737;stop-opacity:1"
+ offset="0"
+ id="stop5208" />
+ <stop
+ id="stop5210"
+ offset="0.2"
+ style="stop-color:#323737;stop-opacity:1" />
+ <stop
+ style="stop-color:#2a2f2f;stop-opacity:1"
+ offset="1"
+ id="stop5212" />
+ </linearGradient>
+ <linearGradient
+ id="light-hover">
+ <stop
+ id="stop4868"
+ offset="0"
+ style="stop-color:#fefefe;stop-opacity:1" />
+ <stop
+ style="stop-color:#fefefe;stop-opacity:1"
+ offset="0.1"
+ id="stop4870" />
+ <stop
+ id="stop4872"
+ offset="1"
+ style="stop-color:#f2f2f1;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="light-normal">
+ <stop
+ style="stop-color:#f4f4f4;stop-opacity:1"
+ offset="0"
+ id="stop4604" />
+ <stop
+ id="stop4610"
+ offset="0.2"
+ style="stop-color:#f4f4f4;stop-opacity:1" />
+ <stop
+ style="stop-color:#e8e8e7;stop-opacity:1"
+ offset="1"
+ id="stop4608" />
+ </linearGradient>
+ <linearGradient
+ id="killme">
+ <stop
+ id="stop5583-0-92-8-0-7-6"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ style="stop-color:#ededed;stop-opacity:1;"
+ offset="0.40000001"
+ id="stop5585-4-7-2-7-9-9" />
+ <stop
+ id="stop5587-6-7-2-0-3-1"
+ offset="1"
+ style="stop-color:#41d3d3;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="dark-hover">
+ <stop
+ id="stop5583-0-92-8-0-7-6-5-1-2-9-5"
+ offset="0"
+ style="stop-color:#393f3f;stop-opacity:1" />
+ <stop
+ style="stop-color:#393f3f;stop-opacity:1"
+ offset="0.2"
+ id="stop5585-4-7-2-7-9-9-92-0-5-3-7" />
+ <stop
+ id="stop5587-6-7-2-0-3-1-21-5-7-8-21"
+ offset="1"
+ style="stop-color:#2f3434;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient8949"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient8951"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient8983"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient8985"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9003"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-normal"
+ id="linearGradient9019"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9021"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-normal"
+ id="linearGradient9037"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9039"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9063"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9065"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9444"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9446"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9448"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9450"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-normal"
+ id="linearGradient9452"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9454"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-normal"
+ id="linearGradient9456"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9458"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9460"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9462"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9464"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-3.911093e-8,-0.58023095)"
+ x1="140.5"
+ y1="50.895367"
+ x2="159.53287"
+ y2="50.895367" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#light-hover"
+ id="linearGradient9466"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-0.50000004,0.40593305)"
+ x1="140.5"
+ y1="49.909203"
+ x2="160.53281"
+ y2="49.909203" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.8754325"
+ inkscape:cx="218"
+ inkscape:cy="1083.5"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ showborder="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1016"
+ inkscape:window-x="1920"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:snap-nodes="true"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-paths="true"
+ inkscape:snap-global="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:bbox-nodes="true"
+ inkscape:object-nodes="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-to-guides="false"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid10919"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ dotted="false"
+ color="#ae00e9"
+ opacity="0.4627451"
+ empcolor="#3f3fff"
+ empopacity="1" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata9897">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:groupmode="layer"
+ id="layer3"
+ inkscape:label="superbackdrop"
+ style="display:none"
+ sodipodi:insensitive="true">
+ <rect
+ ry="0"
+ rx="0"
+ y="-192.36218"
+ x="40"
+ height="440"
+ width="392.87714"
+ id="rect18028"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#eeeeec;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999994;marker:none;enable-background:accumulate"
+ transform="scale(1,-1)" />
+ <rect
+
style="opacity:0.84299999;fill:#3dd3d3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4337"
+ width="24"
+ height="24"
+ x="220"
+ y="-72.637817" />
+ <rect
+ y="-72.637817"
+ x="250"
+ height="24"
+ width="24"
+ id="rect4339"
+
style="opacity:0.84299999;fill:#3dd3d3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
/>
+ <rect
+ transform="scale(1,-1)"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#3a3b39;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999994;marker:none;enable-background:accumulate"
+ id="rect7688"
+ width="392.87714"
+ height="440"
+ x="-370"
+ y="-192.36218"
+ rx="0"
+ ry="0" />
+ </g>
+ <g
+ inkscape:label="assets"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline">
+ <g
+ inkscape:label="#g10801"
+ transform="matrix(1.9947791,0,0,1.9939719,354.38167,165.09433)"
+ id="sjhgfjsgf" />
+ <g
+ id="color-scale-slider-horizontal-hover"
+ inkscape:label="#g9085">
+ <g
+ transform="translate(-42,27)"
+ id="g8969">
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path8971"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path8973"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient8983);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient8985);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+ id="path8975"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path8977"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path8979"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+ id="path8981"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <g
+ id="g8987"
+ transform="matrix(-1,0,0,1,300,27)">
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+ id="path8989"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient9001);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient9003);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+ id="path8991"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path8993"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+ id="path8995"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+ id="path8997"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path8999"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ </g>
+ </g>
+ <g
+ id="color-scale-slider-horizontal"
+ inkscape:label="#g9101">
+ <g
+ id="g9005"
+ transform="translate(-42,52)">
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+ id="path9007"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient9019);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient9021);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+ id="path9009"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9011"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+ id="path9013"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+ id="path9015"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9017"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ </g>
+ <g
+ transform="matrix(-1,0,0,1,300,52)"
+ id="g9023">
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9025"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9027"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient9037);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient9039);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+ id="path9029"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9031"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9033"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+ id="path9035"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ </g>
+ <g
+ id="color-scale-slider-horizontal-active"
+ inkscape:label="#g9067">
+ <g
+ id="g8935"
+ transform="translate(-42,4.9574055e-8)">
+ <g
+ id="g8955">
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path8937"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path8939"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient8949);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient8951);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+ id="path8941"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path8943"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path8945"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#184472;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+ id="path8947"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ </g>
+ <g
+ transform="matrix(-1,0,0,1,300,4.9574055e-8)"
+ id="g9047">
+ <g
+ id="g9049">
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+ id="path9051"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient9063);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient9065);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+ id="path9053"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9055"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+ id="path9057"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+ id="path9059"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9061"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#184472;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ </g>
+ </g>
+ </g>
+ <g
+ id="color-scale-slider-horizontal-insensitive"
+ inkscape:label="#g9311">
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9135"
+ d="M 117.53286,24.287412 98.5,14.862152 98.5107,33.768116 Z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f1f1f1;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f1f1f1;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 140.46714,24.287412 159.5,14.862152 159.4893,33.768116 Z"
+ id="path9151"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <g
+ id="color-scale-slider-horizontal-backdrop"
+ inkscape:label="#g9321">
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#e8e8e7;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 117.53286,-3.712588 98.5,-13.137848 98.5107,5.768116 Z"
+ id="path9303"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ inkscape:label="#path9305"
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9304"
+ d="M 140.46714,-3.712588 159.5,-13.137848 159.4893,5.768116 Z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#e8e8e7;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ </g>
+ <g
+ id="color-scale-slider-horizontal-backdrop-insensitive"
+ inkscape:label="#g9325">
+ <g
+ transform="translate(0,-56)"
+ id="g9315"
+ inkscape:label="#g9311">
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9317"
+ d="M 117.53286,24.287412 98.5,14.862152 98.5107,33.768116 Z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f1f1f1;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f1f1f1;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 140.46714,24.287412 159.5,14.862152 159.4893,33.768116 Z"
+ id="path9319"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ </g>
+ <g
+ inkscape:label="#g9085"
+ id="color-scale-slider-vertical-hover"
+ transform="matrix(0,1,-1,0,296.36218,-273.63782)">
+ <g
+ id="g9332"
+ transform="translate(-42,27)">
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+ id="path9334"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient9444);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient9446);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+ id="path9336"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9338"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+ id="path9340"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+ id="path9342"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9344"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ </g>
+ <g
+ transform="matrix(-1,0,0,1,300,27)"
+ id="g9346">
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9348"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9350"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient9448);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient9450);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+ id="path9352"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9354"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9356"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+ id="path9358"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ </g>
+ <g
+ inkscape:label="#g9101"
+ id="color-scale-slider-vertical"
+ transform="matrix(0,1,-1,0,296.36218,-273.63782)">
+ <g
+ transform="translate(-42,52)"
+ id="g9362">
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9364"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9366"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient9452);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient9454);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+ id="path9368"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9370"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9372"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+ id="path9374"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ <g
+ id="g9376"
+ transform="matrix(-1,0,0,1,300,52)">
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+ id="path9378"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient9456);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient9458);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+ id="path9380"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9382"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+ id="path9384"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+ id="path9386"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9388"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ </g>
+ </g>
+ <g
+ inkscape:label="#g9067"
+ id="color-scale-slider-vertical-active"
+ transform="matrix(0,1,-1,0,296.36218,-273.63782)">
+ <g
+ transform="translate(-42,4.9574055e-8)"
+ id="g9392">
+ <g
+ id="g9394">
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+ id="path9396"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient9460);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient9462);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+ id="path9398"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9400"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+ id="path9402"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+ id="path9404"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9406"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#184472;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ </g>
+ </g>
+ <g
+ id="g9408"
+ transform="matrix(-1,0,0,1,300,4.9574055e-8)">
+ <g
+ id="g9410">
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9412"
+ d="M 159.53286,51.381509 140.5,41.956249 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9414"
+ d="M 159.53286,50.287412 140.5,40.862152 l 0.0107,18.905964 z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient9464);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient9466);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+
style="display:inline;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 157.30918,49.331217 -16.68331,8.352699"
+ id="path9416"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9418"
+ d="m 140.53991,42.100327 18.23961,8.813092"
+
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path9420"
+ d="m 158.41403,49.861543 -17.78816,8.927223"
+
style="display:inline;fill:none;fill-rule:evenodd;stroke:#cfcfcf;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
/>
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#184472;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 159.53286,50.381509 140.5,40.956249 l 0.0107,18.905964 z"
+ id="path9422"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ </g>
+ </g>
+ </g>
+ <g
+ inkscape:label="#g9311"
+ id="color-scale-slider-vertical-insensitive"
+ transform="matrix(0,1,-1,0,296.36218,-273.63782)">
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f1f1f1;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 117.53286,24.287412 98.5,14.862152 98.5107,33.768116 Z"
+ id="path9426"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9428"
+ d="M 140.46714,24.287412 159.5,14.862152 159.4893,33.768116 Z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f1f1f1;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ </g>
+ <g
+ inkscape:label="#g9321"
+ id="color-scale-slider-vertical-backdrop"
+ transform="matrix(0,1,-1,0,296.36218,-273.63782)">
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9432"
+ d="M 117.53286,-3.712588 98.5,-13.137848 98.5107,5.768116 Z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#e8e8e7;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#e8e8e7;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 140.46714,-3.712588 159.5,-13.137848 159.4893,5.768116 Z"
+ id="path9434"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc"
+ inkscape:label="#path9305" />
+ </g>
+ <g
+ inkscape:label="#g9325"
+ id="color-scale-slider-vertical-backdrop-insensitive"
+ transform="matrix(0,1,-1,0,296.36218,-273.63782)">
+ <g
+ inkscape:label="#g9311"
+ id="g9438"
+ transform="translate(0,-56)">
+ <path
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f1f1f1;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 117.53286,24.287412 98.5,14.862152 98.5107,33.768116 Z"
+ id="path9440"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path9442"
+ d="M 140.46714,24.287412 159.5,14.862152 159.4893,33.768116 Z"
+
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f1f1f1;fill-opacity:1;fill-rule:nonzero;stroke:#9d9d99;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
/>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/contrib/gstyle/assets/assets.txt b/contrib/gstyle/assets/assets.txt
new file mode 100644
index 0000000..bf0a6e4
--- /dev/null
+++ b/contrib/gstyle/assets/assets.txt
@@ -0,0 +1,15 @@
+color-scale-slider-horizontal
+color-scale-slider-horizontal-hover
+color-scale-slider-horizontal-active
+color-scale-slider-horizontal-insensitive
+color-scale-slider-horizontal-backdrop
+color-scale-slider-horizontal-backdrop-insensitive
+color-scale-slider-vertical
+color-scale-slider-vertical-hover
+color-scale-slider-vertical-active
+color-scale-slider-vertical-insensitive
+color-scale-slider-vertical-backdrop
+color-scale-slider-vertical-backdrop-insensitive
+gstyle-picker
+unit-percent
+unit-degree
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal-active.png
b/contrib/gstyle/assets/color-scale-slider-horizontal-active.png
new file mode 100644
index 0000000..6eb2cb9
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal-active.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal-active 2 png
b/contrib/gstyle/assets/color-scale-slider-horizontal-active 2 png
new file mode 100644
index 0000000..4800fa2
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal-active 2 png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop-insensitive.png
b/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop-insensitive.png
new file mode 100644
index 0000000..46944de
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop-insensitive.png
differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop-insensitive 2 png
b/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop-insensitive 2 png
new file mode 100644
index 0000000..eeadf0d
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop-insensitive 2 png
differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop.png
b/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop.png
new file mode 100644
index 0000000..711396e
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop 2 png
b/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop 2 png
new file mode 100644
index 0000000..fb3421e
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal-backdrop 2 png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal-hover.png
b/contrib/gstyle/assets/color-scale-slider-horizontal-hover.png
new file mode 100644
index 0000000..a71a509
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal-hover.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal-hover 2 png
b/contrib/gstyle/assets/color-scale-slider-horizontal-hover 2 png
new file mode 100644
index 0000000..cbe7dda
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal-hover 2 png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal-insensitive.png
b/contrib/gstyle/assets/color-scale-slider-horizontal-insensitive.png
new file mode 100644
index 0000000..46944de
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal-insensitive.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal-insensitive 2 png
b/contrib/gstyle/assets/color-scale-slider-horizontal-insensitive 2 png
new file mode 100644
index 0000000..eeadf0d
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal-insensitive 2 png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal.png
b/contrib/gstyle/assets/color-scale-slider-horizontal.png
new file mode 100644
index 0000000..972ea4d
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-horizontal 2 png
b/contrib/gstyle/assets/color-scale-slider-horizontal 2 png
new file mode 100644
index 0000000..f353f2c
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-horizontal 2 png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical-active.png
b/contrib/gstyle/assets/color-scale-slider-vertical-active.png
new file mode 100644
index 0000000..98f4b04
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical-active.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical-active 2 png
b/contrib/gstyle/assets/color-scale-slider-vertical-active 2 png
new file mode 100644
index 0000000..dc33262
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical-active 2 png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical-backdrop-insensitive.png
b/contrib/gstyle/assets/color-scale-slider-vertical-backdrop-insensitive.png
new file mode 100644
index 0000000..521aaae
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical-backdrop-insensitive.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical-backdrop-insensitive 2 png
b/contrib/gstyle/assets/color-scale-slider-vertical-backdrop-insensitive 2 png
new file mode 100644
index 0000000..ebc05d2
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical-backdrop-insensitive 2 png
differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical-backdrop.png
b/contrib/gstyle/assets/color-scale-slider-vertical-backdrop.png
new file mode 100644
index 0000000..fa72ee5
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical-backdrop.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical-backdrop 2 png
b/contrib/gstyle/assets/color-scale-slider-vertical-backdrop 2 png
new file mode 100644
index 0000000..6b5054e
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical-backdrop 2 png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical-hover.png
b/contrib/gstyle/assets/color-scale-slider-vertical-hover.png
new file mode 100644
index 0000000..e155e21
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical-hover.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical-hover 2 png
b/contrib/gstyle/assets/color-scale-slider-vertical-hover 2 png
new file mode 100644
index 0000000..9d0a254
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical-hover 2 png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical-insensitive.png
b/contrib/gstyle/assets/color-scale-slider-vertical-insensitive.png
new file mode 100644
index 0000000..521aaae
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical-insensitive.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical-insensitive 2 png
b/contrib/gstyle/assets/color-scale-slider-vertical-insensitive 2 png
new file mode 100644
index 0000000..ebc05d2
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical-insensitive 2 png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical.png
b/contrib/gstyle/assets/color-scale-slider-vertical.png
new file mode 100644
index 0000000..be639a0
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical.png differ
diff --git a/contrib/gstyle/assets/color-scale-slider-vertical 2 png
b/contrib/gstyle/assets/color-scale-slider-vertical 2 png
new file mode 100644
index 0000000..cc0b33f
Binary files /dev/null and b/contrib/gstyle/assets/color-scale-slider-vertical 2 png differ
diff --git a/contrib/gstyle/assets/eyedropper-symbolic.svg b/contrib/gstyle/assets/eyedropper-symbolic.svg
new file mode 100644
index 0000000..54adac2
--- /dev/null
+++ b/contrib/gstyle/assets/eyedropper-symbolic.svg
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="15.999982"
+ height="16.000017"
+ viewBox="0 0 15.999982 16.000017"
+ id="svg4370"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="eyedropper-symbolic.svg">
+ <defs
+ id="defs4372" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#3a3b39"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="32"
+ inkscape:cx="7.453515"
+ inkscape:cy="10.78978"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1043"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ units="px"
+ showborder="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:object-nodes="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4918"
+ empcolor="#3ff5ff"
+ empopacity="0.56862745"
+ color="#36ebff"
+ opacity="0.32941176"
+ dotted="false"
+ originx="-335.5638"
+ originy="-859.57305" />
+ <sodipodi:guide
+ position="-31.003958,3.4383656"
+ orientation="1,0"
+ id="guide4174" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4375">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-335.56379,-176.78911)">
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill:#ffffff;fill-rule:evenodd;stroke-width:0.85961121;stroke-linecap:round;stroke-linejoin:round;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 345.69922,179.88672 a 0.42984859,0.42984859 0 0 0 -0.29492,0.1289 l -9.39649,9.3711 a
0.42984859,0.42984859 0 0 0 -0.10742,0.17578 c -0.0937,0.30197 -0.14933,0.77018 -0.33008,1.89258 a
0.43027012,0.43027012 0 0 0 0.84961,0.13672 c 0.17297,-1.07406 0.25709,-1.57187 0.28711,-1.69141 l
9.30274,-9.27539 a 0.42984859,0.42984859 0 0 0 -0.31055,-0.73828 z"
+ id="path4190"
+ inkscape:connector-curvature="0" />
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill:#ffffff;fill-rule:evenodd;stroke-width:0.85961121;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 349.59766,176.80664 c -0.50881,-0.0598 -1.0717,0.0201 -1.47071,0.41797 l -1.42578,1.42187
0.60742,0.60938 1.42579,-1.42188 c 0.13448,-0.13411 0.4246,-0.21348 0.76171,-0.17382 0.33618,0.0395
0.68426,0.20133 0.83789,0.35351 4.4e-4,4.3e-4 0.002,0.002 0.002,0.002 0.1526,0.15322 0.31488,0.50186
0.35547,0.8379 0.0407,0.33698 -0.0364,0.62656 -0.16993,0.75976 l -1.42578,1.42188 0.60742,0.60742
1.42579,-1.42188 c 0.39991,-0.3988 0.47748,-0.96173 0.41601,-1.4707 -0.0614,-0.50808 -0.25728,-0.9955
-0.60351,-1.3418 -4.2e-4,-4.2e-4 -0.002,4.2e-4 -0.002,0 -0.34729,-0.34562 -0.8336,-0.54178 -1.34179,-0.60156
z"
+ id="path4194"
+ inkscape:connector-curvature="0" />
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill:#ffffff;fill-rule:evenodd;stroke-width:1.36000001;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 345.77734,177.14844 c -0.34753,-0.0394 -0.69207,0.0483 -0.95507,0.28711 -0.26301,0.23879
-0.40365,0.61407 -0.3711,0.97461 0.0326,0.36054 0.21384,0.69204 0.48828,0.9707 l 0.004,0.004 2.07031,2.04687
0,0.002 1.9668,1.93946 c 0.27935,0.27363 0.6111,0.45311 0.9668,0.49023 0.3557,0.0371 0.72778,-0.0855
0.97656,-0.33984 0.49756,-0.50878 0.37835,-1.3837 -0.16797,-1.9336 l -1.99414,-2.00781 -0.1211,0.11914
0.11915,-0.12109 -2.00586,-1.95508 c -0.27752,-0.27048 -0.62903,-0.4372 -0.97657,-0.47656 z"
+ id="path4196"
+ inkscape:connector-curvature="0" />
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill:#ffffff;fill-rule:evenodd;stroke-width:0.85961121;stroke-linecap:round;stroke-linejoin:round;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 348.01758,182.19922 a 0.42984859,0.42984859 0 0 0 -0.29492,0.1289 l -9.33008,9.30665 c
-0.0297,0.0113 -0.1779,0.0563 -0.44727,0.10156 -0.28716,0.0483 -0.68807,0.10677 -1.20312,0.19922 a
0.42984859,0.42984859 0 1 0 0.15234,0.8457 c 0.49964,-0.0897 0.88793,-0.14591 1.19336,-0.19727
0.30543,-0.0514 0.52205,-0.0809 0.74805,-0.20507 a 0.42984859,0.42984859 0 0 0 0.0976,-0.0723 l
9.39649,-9.36914 a 0.42984859,0.42984859 0 0 0 -0.3125,-0.73828 z"
+ id="path4198"
+ inkscape:connector-curvature="0" />
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill:#ffffff;fill-rule:evenodd;stroke-width:1.05546677;stroke-linecap:square;stroke-linejoin:round;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 340.54883,186.57227 a 0.52778616,0.52778616 0 0 0 -0.36524,0.14648 c -0.75207,0.72292
-2.13158,2.23025 -3.19336,3.26367 a 0.52778616,0.52778616 0 0 0 -0.15625,0.3125 l -0.0918,0.72461 a
0.52778616,0.52778616 0 0 0 0.58203,0.58985 l 0.70117,-0.0781 a 0.52778616,0.52778616 0 0 0 0.31445,-0.15039
l 3.91797,-3.9082 a 0.52778616,0.52778616 0 0 0 -0.37109,-0.90039 l -1.33789,0 z"
+ id="path4210"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/contrib/gstyle/assets/render-assets-symbolic.py b/contrib/gstyle/assets/render-assets-symbolic.py
new file mode 100755
index 0000000..0fb4d00
--- /dev/null
+++ b/contrib/gstyle/assets/render-assets-symbolic.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python3
+
+import os
+import subprocess
+import shutil
+
+INKSCAPE = "/usr/bin/inkscape"
+SVG_SRC = "assets-symbolic.svg"
+TXT_SRC = "assets-symbolic.txt"
+
+CWD = os.getcwd()
+
+class Exporter(object):
+ def __init__(self):
+ self.ids = []
+ with open(TXT_SRC) as input_file:
+ for line in input_file:
+ self.ids.append(line.strip())
+
+ def start(self):
+ for id in self.ids:
+ self.export (id)
+ print("id:'{}' exported".format(id))
+
+ def export(self, id):
+ src = "".join([CWD, "/", SVG_SRC])
+ dst = "".join([CWD, "/", id, ".svg"])
+ shutil.copyfile (src, dst)
+ self.call_inkscape([dst,
+ "--select={}".format(id),
+ "--verb=FitCanvasToSelection",
+ "--verb=EditInvertInAllLayers",
+ "--verb=EditDelete",
+ "--verb=EditSelectAll",
+ "--verb=SelectionUnGroup",
+ "--verb=SelectionUnGroup",
+ "--verb=SelectionUnGroup",
+ "--verb=StrokeToPath",
+ "--verb=FileVacuum",
+ "--verb=FileSave",
+ "--verb=FileClose",
+ "--verb=FileQuit"])
+
+ def call_inkscape(self, cmd):
+ cmd.insert(0, INKSCAPE)
+ subprocess.run(cmd, stdout=subprocess.PIPE)
+
+if __name__ == "__main__":
+ app = Exporter()
+ app.start()
diff --git a/contrib/gstyle/assets/render-assets.sh b/contrib/gstyle/assets/render-assets.sh
new file mode 100755
index 0000000..da1b240
--- /dev/null
+++ b/contrib/gstyle/assets/render-assets.sh
@@ -0,0 +1,34 @@
+#! /bin/bash
+
+INKSCAPE="/usr/bin/inkscape"
+OPTIPNG="/usr/bin/optipng"
+
+SRC_FILE="assets.svg"
+ASSETS_DIR="."
+INDEX="assets.txt"
+
+for i in `cat $INDEX`
+do
+if [ -f $ASSETS_DIR/$i.png ]; then
+ echo $ASSETS_DIR/$i.png exists.
+else
+ echo
+ echo Rendering $ASSETS_DIR/$i.png
+ $INKSCAPE --export-id=$i \
+ --export-id-only \
+ --export-png=$ASSETS_DIR/$i.png $SRC_FILE >/dev/null
+ $OPTIPNG -o7 --quiet $ASSETS_DIR/$i.png
+fi
+if [ -f $ASSETS_DIR/$i 2 png ]; then
+ echo $ASSETS_DIR/$i 2 png exists.
+else
+ echo
+ echo Rendering $ASSETS_DIR/$i 2 png
+ $INKSCAPE --export-id=$i \
+ --export-dpi=180 \
+ --export-id-only \
+ --export-png=$ASSETS_DIR/$i 2 png $SRC_FILE >/dev/null #\
+ $OPTIPNG -o7 --quiet $ASSETS_DIR/$i 2 png
+fi
+done
+exit 0
diff --git a/contrib/gstyle/assets/unit-degree-symbolic.svg b/contrib/gstyle/assets/unit-degree-symbolic.svg
new file mode 100644
index 0000000..75c867f
--- /dev/null
+++ b/contrib/gstyle/assets/unit-degree-symbolic.svg
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="13"
+ height="16"
+ viewBox="0 0 13 16"
+ id="svg4370"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="unit-degree-symbolic.svg">
+ <defs
+ id="defs4372" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#3a3b39"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="32"
+ inkscape:cx="-12.98269"
+ inkscape:cy="-9.63714"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1043"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ units="px"
+ showborder="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:object-nodes="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4918"
+ empcolor="#3ff5ff"
+ empopacity="0.56862745"
+ color="#36ebff"
+ opacity="0.32941176"
+ dotted="false"
+ originx="-356"
+ originy="-879.99998" />
+ <sodipodi:guide
+ position="-51.440163,-16.988554"
+ orientation="1,0"
+ id="guide4174" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4375">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-356,-156.3622)">
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill:#bebebe;fill-rule:evenodd;stroke-width:0.89999998;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 360.1875,157.36133 c -1.75494,0 -3.1875,1.43256 -3.1875,3.1875 0,1.75493 1.43256,3.1875
3.1875,3.1875 1.75494,0 3.1875,-1.43257 3.1875,-3.1875 0,-1.75494 -1.43256,-3.1875 -3.1875,-3.1875 z m
0,0.90039 c 1.26854,0 2.28711,1.01857 2.28711,2.28711 0,1.26854 -1.01857,2.28711 -2.28711,2.28711 -1.26854,0
-2.28711,-1.01857 -2.28711,-2.28711 0,-1.26854 1.01857,-2.28711 2.28711,-2.28711 z"
+ id="circle7708"
+ inkscape:connector-curvature="0" />
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill-rule:evenodd;stroke-width:0;stroke-linecap:round;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 355.98438,156.3457 0,0.0156 0,16.01758 13.03124,0 0,-16.03321 -13.03124,0 z m 0.0312,0.0332
12.96876,0 0,15.96679 -12.96876,0 0,-15.96679 z"
+ id="rect9609"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/contrib/gstyle/assets/unit-percent-symbolic.svg b/contrib/gstyle/assets/unit-percent-symbolic.svg
new file mode 100644
index 0000000..786854b
--- /dev/null
+++ b/contrib/gstyle/assets/unit-percent-symbolic.svg
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="13"
+ height="16"
+ viewBox="0 0 13 16"
+ id="svg4370"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="unit-percent-symbolic.svg">
+ <defs
+ id="defs4372" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#3a3b39"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="32"
+ inkscape:cx="6.8947754"
+ inkscape:cy="-9.6371447"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1920"
+ inkscape:window-height="1043"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-paths="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true"
+ units="px"
+ showborder="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:snap-midpoints="true"
+ inkscape:object-nodes="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4918"
+ empcolor="#3ff5ff"
+ empopacity="0.56862745"
+ color="#36ebff"
+ opacity="0.32941176"
+ dotted="false"
+ originx="-336.12253"
+ originy="-879.99998" />
+ <sodipodi:guide
+ position="-31.562698,-16.988559"
+ orientation="1,0"
+ id="guide4174" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4375">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-336.12253,-156.3622)">
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill:#bebebe;fill-rule:evenodd;stroke-width:0.89999998;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 340.31055,157.36133 c -1.75494,0 -3.1875,1.43256 -3.1875,3.1875 0,1.75493 1.43256,3.1875
3.1875,3.1875 1.75493,0 3.18554,-1.43257 3.18554,-3.1875 0,-1.75494 -1.43061,-3.1875 -3.18554,-3.1875 z m
0,0.90039 c 1.26854,0 2.28711,1.01857 2.28711,2.28711 0,1.26854 -1.01857,2.28711 -2.28711,2.28711 -1.26854,0
-2.28711,-1.01857 -2.28711,-2.28711 0,-1.26854 1.01857,-2.28711 2.28711,-2.28711 z"
+ id="circle7700"
+ inkscape:connector-curvature="0" />
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill:#bebebe;fill-rule:evenodd;stroke-width:0.90353149;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 344.92578,164.95898 c -1.76098,0 -3.19922,1.43852 -3.19922,3.20118 0,1.76266 1.43824,3.20117
3.19922,3.20117 1.76099,0 3.19727,-1.43851 3.19727,-3.20117 0,-1.76266 -1.43628,-3.20118 -3.19727,-3.20118 z
m 0,0.90235 c 1.27189,0 2.29297,1.02368 2.29297,2.29883 0,1.27515 -1.02108,2.29882 -2.29297,2.29882
-1.27188,0 -2.29492,-1.02367 -2.29492,-2.29882 0,-1.27515 1.02304,-2.29883 2.29492,-2.29883 z"
+ id="ellipse7702"
+ inkscape:connector-curvature="0" />
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill:#bebebe;fill-rule:evenodd;stroke-width:0.71361196;stroke-linecap:round;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 347.09961,158.00195 a 0.35684166,0.35684166 0 0 0 -0.26172,0.14649 l -9,12 a
0.35684166,0.35684166 0 1 0 0.57031,0.42773 l 9,-12 a 0.35684166,0.35684166 0 0 0 -0.30859,-0.57422 z"
+ id="path7704"
+ inkscape:connector-curvature="0" />
+ <path
+
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;overflow:visible;color-interpolation-filters:linearRGB;solid-opacity:1;fill-rule:evenodd;stroke-width:0;stroke-linecap:round;image-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 336.10742,156.3457 0,0.0156 0,16.01758 13.03125,0 0,-16.03321 -13.03125,0 z m 0.0312,0.0332
12.96875,0 0,15.96679 -12.96875,0 0,-15.96679 z"
+ id="rect9598"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/contrib/gstyle/check-palette.sh b/contrib/gstyle/check-palette.sh
new file mode 100755
index 0000000..72fcdba
--- /dev/null
+++ b/contrib/gstyle/check-palette.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# "./check-palette.sh files..." will validate files given on command line.
+# "./check-palette.sh" without arguments will validate all palette files
+# in the ./data/palettes directory
+
+files=""
+
+if [ $1 ]; then
+ files=$@
+else
+ cd ./data/palettes
+ files=*.xml
+fi
+
+for file in $files; do
+ xmllint --relaxng palette.rng --noout $file || exit 1
+done
diff --git a/contrib/gstyle/colorlexer-regen.sh b/contrib/gstyle/colorlexer-regen.sh
new file mode 100755
index 0000000..8b13702
--- /dev/null
+++ b/contrib/gstyle/colorlexer-regen.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+RE2C="re2c"
+MIN_VERSION="0.13.7"
+MIN_MAJOR_VERSION="0"
+MIN_MINOR_VERSION="13"
+MIN_MICRO_VERSION="7"
+
+BASE_DIR=$(dirname "$0")
+
+if [ ! hash $RE2C 2>/dev/null ]; then
+ echo "Can't find re2c program. You need to install it in a version >= $version"
+ exit 1
+fi
+
+CURRENT_VERSION=$(echo `re2c -v`| cut -f2 -d " ")
+CURRENT_MAJOR_VERSION=$(echo "$CURRENT_VERSION" | cut -f1 -d.)
+CURRENT_MINOR_VERSION=$(echo "$CURRENT_VERSION" | cut -f2 -d.)
+CURRENT_MICRO_VERSION=$(echo "$CURRENT_VERSION" | cut -f3 -d.)
+
+if [ $CURRENT_MICRO_VERSION="" ]; then
+ CURRENT_MICRO_VERSION="0"
+fi
+
+echo "re2c current version found: $CURRENT_VERSION"
+
+if [ $CURRENT_MAJOR_VERSION -lt $MIN_MAJOR_VERSION ]; then
+ if [ $CURRENT_MINOR_VERSION -lt $MIN_MINOR_VERSION ]; then
+ if [ $CURRENT_MICRO_VERSION -lt $MIN_MICRO_VERSION ]; then
+ echo "You need a re2c version of at least $MIN_VERSION"
+ exit 1
+ fi
+ fi
+fi
+
+`re2c ${BASE_DIR}/gstyle-colorlexer.re -b -s -8 -i -o ${BASE_DIR}/gstyle-colorlexer.c`
+
+echo "gstyle-colorlexer.re compiled in gstyle-colorlexer.c"
diff --git a/contrib/gstyle/data/palettes/basic.xml b/contrib/gstyle/data/palettes/basic.xml
new file mode 100644
index 0000000..f62c42d
--- /dev/null
+++ b/contrib/gstyle/data/palettes/basic.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+-->
+<palette id="basic" _name="Basic">
+
+ <color name="color_hex6" value="#808080"/>
+ <color name="color_hex3" value="#1aF"/>
+ <color name="color_rgb" value="rgb(100, 200, 50)"/>
+ <color name="color_rgb_percent" value="rgb(10%, 50%, 70%)"/>
+ <color name="color_rgba" value="rgba(0, 10, 70, 1)"/>
+ <color name="color_rgba_percent" value="rgba(10%, 50%, 40%, 0.5)"/>
+ <color name="color_hsl" value="hsl(100, 100%, 50%)"/>
+ <color name="color_hsla" value="hsla(400, 50%, 40%, 0.5)"/>
+ <color name="color_named" value="aliceblue"/>
+
+</palette>
diff --git a/contrib/gstyle/data/palettes/palette.rng b/contrib/gstyle/data/palettes/palette.rng
new file mode 100644
index 0000000..6046745
--- /dev/null
+++ b/contrib/gstyle/data/palettes/palette.rng
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+-->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
+ ns="">
+
+<define name="color_hex6">
+ <attribute name="value">
+ <data type="string">
+ <param name="pattern">#([0-9a-fA-F]+){6}</param>
+ </data>
+ </attribute>
+</define>
+
+<define name="color_hex3">
+ <attribute name="value">
+ <data type="string">
+ <param name="pattern">#([0-9a-fA-F]+){3}</param>
+ </data>
+ </attribute>
+</define>
+
+<define name="color_rgb">
+ <attribute name="value">
+ <data type="string">
+ <param name="pattern">rgb\((\s*[0-9]*\.?[0-9]+\s*,){2}\s*[0-9]*\.?[0-9]+\s*\)</param>
+ </data>
+ </attribute>
+</define>
+
+<define name="color_rgb_percent">
+ <attribute name="value">
+ <data type="string">
+ <param name="pattern">rgb\((\s*[0-9]*\.?[0-9]+%\s*,){2}\s*[0-9]*\.?[0-9]+%\s*\)</param>
+ </data>
+ </attribute>
+</define>
+
+<define name="color_rgba">
+ <attribute name="value">
+ <data type="string">
+ <param name="pattern">rgba\((\s*[0-9]*\.?[0-9]+\s*,){3}\s*[0-9]*\.?[0-9]+\s*\)</param>
+ </data>
+ </attribute>
+</define>
+
+<define name="color_rgba_percent">
+ <attribute name="value">
+ <data type="string">
+ <param name="pattern">rgba\((\s*[0-9]*\.?[0-9]+%\s*,){3}\s*[0-9]*\.?[0-9]+\s*\)</param>
+ </data>
+ </attribute>
+
+</define>
+ <define name="color_hsl">
+ <attribute name="value">
+ <data type="string">
+ <param name="pattern">hsl\(\s*[0-9]*\.?[0-9]+\s*(,\s*[0-9]*\.?[0-9]+%\s*){2}\)</param>
+ </data>
+ </attribute>
+</define>
+
+<define name="color_hsla">
+ <attribute name="value">
+ <data type="string">
+ <param
name="pattern">hsla\(\s*[0-9]*\.?[0-9]+\s*(,\s*[0-9]*\.?[0-9]+%\s*){2},\s*[0-9]*\.?[0-9]+\s*\)</param>
+ </data>
+ </attribute>
+</define>
+
+<!-- TODO: add named colors list to the pattern -->
+<define name="color_named">
+ <attribute name="value">
+ <data type="string">
+ <param name="pattern">[a-zA-Z]+</param>
+ </data>
+ </attribute>
+</define>
+
+<start>
+ <element name="palette">
+ <attribute name="id">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_]+</param>
+ </data>
+ </attribute>
+ <choice>
+ <attribute name="name"/>
+ <attribute name="_name"/>
+ </choice>
+ <optional>
+ <oneOrMore>
+ <element name="color">
+ <attribute name="name">
+ <data type="string">
+ <param name="pattern">[a-zA-Z0-9_]+</param>
+ </data>
+ </attribute>
+ <choice>
+ <ref name="color_hex6"/>
+ <ref name="color_hex3"/>
+ <ref name="color_rgb"/>
+ <ref name="color_rgb_percent"/>
+ <ref name="color_rgba"/>
+ <ref name="color_rgba_percent"/>
+ <ref name="color_hsl"/>
+ <ref name="color_hsla"/>
+ <ref name="color_named"/>
+ </choice>
+ </element>
+ </oneOrMore>
+ </optional>
+ </element>
+</start>
+
+</grammar>
+
diff --git a/contrib/gstyle/gstyle-animation.c b/contrib/gstyle/gstyle-animation.c
new file mode 100644
index 0000000..dc63096
--- /dev/null
+++ b/contrib/gstyle/gstyle-animation.c
@@ -0,0 +1,51 @@
+/* gstyle-animation.c
+ *
+ * Copyright (C) 2016 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtk/gtk.h>
+
+#include "gstyle-animation.h"
+
+gdouble
+gstyle_animation_ease_in_out_cubic (gdouble offset)
+{
+ gdouble val;
+
+ if (offset < 0.5)
+ return (offset * offset * offset * 4.0);
+ else
+ {
+ val = (offset - 0.5) * 2.0;
+ val = val - 1.0;
+ val = val * val * val + 1.0;
+ val = val / 2.0 + 0.5;
+
+ return val;
+ }
+}
+
+gboolean
+gstyle_animation_check_enable_animation (void)
+{
+ gboolean enable_animation;
+
+ g_object_get (gtk_settings_get_default (),
+ "gtk-enable-animations", &enable_animation,
+ NULL);
+
+ return enable_animation;
+}
diff --git a/contrib/gstyle/gstyle-animation.h b/contrib/gstyle/gstyle-animation.h
new file mode 100644
index 0000000..a28be56
--- /dev/null
+++ b/contrib/gstyle/gstyle-animation.h
@@ -0,0 +1,31 @@
+/* gstyle-animation.h
+ *
+ * Copyright (C) 2016 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_ANIMATION_H
+#define GSTYLE_ANIMATION_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gdouble gstyle_animation_ease_in_out_cubic (gdouble offset);
+gboolean gstyle_animation_check_enable_animation (void);
+
+G_END_DECLS
+
+#endif /* GSTYLE_ANIMATION_H */
diff --git a/contrib/gstyle/gstyle-cielab.c b/contrib/gstyle/gstyle-cielab.c
new file mode 100644
index 0000000..631d479
--- /dev/null
+++ b/contrib/gstyle/gstyle-cielab.c
@@ -0,0 +1,64 @@
+/* gstyle-cielab.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-cielab"
+
+#include "gstyle-cielab.h"
+
+G_DEFINE_BOXED_TYPE (GstyleCielab, gstyle_cielab, gstyle_cielab_copy, gstyle_cielab_free)
+
+/**
+ * GstyleCielab:
+ * @l: color- lightness dimension from 0 (darkest black) to 100 (brightest white)
+ * @a: color-opponent dimension from green (-300) to red (+299).
+ * @b: color-opponent dimension from blue (-300) to yellow (+299).
+ * @alpha: The opacity of the color in [0, 1] range.
+ *
+ * A #GstyleCielab used to represent a color in
+ * the CIE L*a*b* 1976 colorspace.
+ */
+
+/**
+ * gstyle_cielab_copy:
+ * @self: a #GstyleCielab
+ *
+ * Makes a copy of a #GstyleCielab.
+ *
+ * The result must be freed through gstyle_cielab_free().
+ *
+ * Returns: a newly allocated #GstyleCielab, with the same contents as @self
+ *
+ */
+GstyleCielab *
+gstyle_cielab_copy (const GstyleCielab *self)
+{
+ return g_slice_dup (GstyleCielab, self);
+}
+
+/**
+ * gstyle_cielab_free:
+ * @self: a #GstyleCielab
+ *
+ * Frees a #GstyleCielab created with gstyle_cielab_copy().
+ *
+ */
+void
+gstyle_cielab_free (GstyleCielab *self)
+{
+ g_slice_free (GstyleCielab, self);
+}
diff --git a/contrib/gstyle/gstyle-cielab.h b/contrib/gstyle/gstyle-cielab.h
new file mode 100644
index 0000000..32acaef
--- /dev/null
+++ b/contrib/gstyle/gstyle-cielab.h
@@ -0,0 +1,45 @@
+/* gstyle-cielab.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_CIELAB_H
+#define GSTYLE_CIELAB_H
+
+#include <glib.h>
+
+#include "gstyle-types.h"
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_CIELAB (gstyle_cielab_get_type ())
+
+struct _GstyleCielab
+{
+ gdouble l;
+ gdouble a;
+ gdouble b;
+ gdouble alpha;
+};
+
+GType gstyle_cielab_get_type (void) G_GNUC_CONST;
+
+GstyleCielab *gstyle_cielab_copy (const GstyleCielab *self);
+void gstyle_cielab_free (GstyleCielab *self);
+
+G_END_DECLS
+
+#endif /* GSTYLE_CIELAB_H */
diff --git a/contrib/gstyle/gstyle-color-component.c b/contrib/gstyle/gstyle-color-component.c
new file mode 100644
index 0000000..e88dec3
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-component.c
@@ -0,0 +1,50 @@
+/* gstyle-color-component.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtk/gtk.h>
+
+#include "gstyle-color-component.h"
+
+GType
+gstyle_color_component_get_type (void)
+{
+ static GType type_id;
+ static const GEnumValue values[] = {
+ { GSTYLE_COLOR_COMPONENT_HSV_H, "GSTYLE_COLOR_COMPONENT_HSV_H", "hsv_h" },
+ { GSTYLE_COLOR_COMPONENT_HSV_S, "GSTYLE_COLOR_COMPONENT_HSV_S", "hsv_s" },
+ { GSTYLE_COLOR_COMPONENT_HSV_V, "GSTYLE_COLOR_COMPONENT_HSV_V", "hsv_v" },
+ { GSTYLE_COLOR_COMPONENT_LAB_L, "GSTYLE_COLOR_COMPONENT_LAB_L", "lab_l" },
+ { GSTYLE_COLOR_COMPONENT_LAB_A, "GSTYLE_COLOR_COMPONENT_LAB_A", "lab_a" },
+ { GSTYLE_COLOR_COMPONENT_LAB_B, "GSTYLE_COLOR_COMPONENT_LAB_B", "lab_b" },
+ { GSTYLE_COLOR_COMPONENT_RGB_RED, "GSTYLE_COLOR_COMPONENT_RGB_RED", "rgb_red" },
+ { GSTYLE_COLOR_COMPONENT_RGB_GREEN, "GSTYLE_COLOR_COMPONENT_RGB_GREEN", "rgb_green" },
+ { GSTYLE_COLOR_COMPONENT_RGB_BLUE, "GSTYLE_COLOR_COMPONENT_RGB_BLUE", "rgb_blue" },
+ { GSTYLE_COLOR_COMPONENT_NONE, "GSTYLE_COLOR_COMPONENT_NONE", "none" },
+ { 0 }
+ };
+
+ if (g_once_init_enter (&type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_enum_register_static ("GstyleColorComponent", values);
+ g_once_init_leave (&type_id, _type_id);
+ }
+
+ return type_id;
+}
diff --git a/contrib/gstyle/gstyle-color-component.h b/contrib/gstyle/gstyle-color-component.h
new file mode 100644
index 0000000..a7f694e
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-component.h
@@ -0,0 +1,45 @@
+/* gstyle-color-component.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_COMPONENT_H
+#define GSTYLE_COLOR_COMPONENT_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_COLOR_COMPONENT (gstyle_color_component_get_type()
+
+typedef enum {
+ GSTYLE_COLOR_COMPONENT_HSV_H,
+ GSTYLE_COLOR_COMPONENT_HSV_S,
+ GSTYLE_COLOR_COMPONENT_HSV_V,
+ GSTYLE_COLOR_COMPONENT_LAB_L,
+ GSTYLE_COLOR_COMPONENT_LAB_A,
+ GSTYLE_COLOR_COMPONENT_LAB_B,
+ GSTYLE_COLOR_COMPONENT_RGB_RED,
+ GSTYLE_COLOR_COMPONENT_RGB_GREEN,
+ GSTYLE_COLOR_COMPONENT_RGB_BLUE,
+ N_GSTYLE_COLOR_COMPONENT,
+ GSTYLE_COLOR_COMPONENT_NONE
+} GstyleColorComponent;
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_COMPONENT_H */
+
diff --git a/contrib/gstyle/gstyle-color-convert.c b/contrib/gstyle/gstyle-color-convert.c
new file mode 100644
index 0000000..33d5d82
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-convert.c
@@ -0,0 +1,843 @@
+/* gstyle-color-convert.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "gstyle-color-convert.h"
+
+#define _2PI 6.2831853071795864769252867665590057683943387987502
+#define PI_d_6 0.523598775598298873077107230546583814032861566562516
+#define PI_d_30 0.104719755119659774615421446109316762806572313312503
+#define _63PI_d_180 1.099557428756427633461925184147826009469009289781285
+#define _180_d_PI 57.29577951308232087679815481410517033240547246656442
+#define _25_pow_7 6103515625
+#define _30PI_d_180 0.523598775598298873077107230546583814032861566562516
+#define _16_d_116 0.137931034
+
+#define one_third 0.333333333333333333333333333333333333333333333333333
+#define two_third 0.666666666666666666666666666666666666666666666666666
+
+#define D65_xref 0.95047
+#define D65_yref 1.0
+#define D65_zref 1.08883
+
+/* DeltaE algorithm described at:
+ * http://www.ece.rochester.edu/~gsharma/ciede2000/ciede2000noteCRNA.pdf
+ *
+ */
+
+/* pow_1_24 and pow_24 are adapted version from babl, published under LGPL */
+/* babl - dynamically extendable universal pixel conversion library.
+ * Copyright (C) 2012, Red Hat, Inc.
+ */
+
+/* Chebychev polynomial terms for x^(5/12) expanded around x=1.5
+ * Non-zero terms calculated via
+ * NIntegrate[(2/Pi)*ChebyshevT[N,u]/Sqrt [1-u^2]*((u+3)/2)^(5/12),{u,-1,1}, PrecisionGoal->20,
WorkingPrecision -> 100]
+ * Zeroth term is similar except it uses 1/pi rather than 2/pi.
+ */
+static const double Cn[] = {
+ 1.1758200232996901923,
+ 0.16665763094889061230,
+ -0.0083154894939042125035,
+ 0.00075187976780420279038,
+ -0.000083240178519391795367,
+ 0.000010229209410070008679,
+ -1.3401001466409860246e-6,
+ 1.8333422241635376682e-7,
+ -2.5878596761348859722e-8
+};
+
+/* Returns x^(5/12) for x in [1,2) */
+static inline gdouble
+pow512norm (gdouble x)
+{
+ gdouble Tn[9];
+ gdouble u;
+
+ u = 2.0*x - 3.0;
+ Tn[0] = 1.0;
+ Tn[1] = u;
+ Tn[2] = 2*u*Tn[2-1] - Tn[2-2];
+ Tn[3] = 2*u*Tn[3-1] - Tn[3-2];
+ Tn[4] = 2*u*Tn[4-1] - Tn[4-2];
+ Tn[5] = 2*u*Tn[5-1] - Tn[5-2];
+ Tn[6] = 2*u*Tn[6-1] - Tn[6-2];
+
+ return Cn[0]*Tn[0] + Cn[1]*Tn[1] + Cn[2]*Tn[2] + Cn[3]*Tn[3] + Cn[4]*Tn[4] + Cn[5]*Tn[5] + Cn[6]*Tn[6];
+}
+
+/* Precalculated (2^N) ^ (5 / 12) */
+static const gdouble pow2_512[12] =
+{
+ 1.0,
+ 1.3348398541700343678,
+ 1.7817974362806785482,
+ 2.3784142300054420538,
+ 3.1748021039363991669,
+ 4.2378523774371812394,
+ 5.6568542494923805819,
+ 7.5509945014535482244,
+ 1.0079368399158985525e1,
+ 1.3454342644059433809e1,
+ 1.7959392772949968275e1,
+ 2.3972913230026907883e1
+};
+
+/* Returns x^(1/2.4) == x^(5/12) */
+static inline gdouble
+pow_1_24 (gdouble x)
+{
+ gdouble s;
+ gint iexp;
+ div_t qr = {0};
+
+ s = frexp (x, &iexp);
+ s *= 2.0;
+ iexp -= 1;
+
+ qr = div (iexp, 12);
+ if (qr.rem < 0)
+ {
+ qr.quot -= 1;
+ qr.rem += 12;
+ }
+
+ return ldexp (pow512norm (s) * pow2_512[qr.rem], 5 * qr.quot);
+}
+
+/* Chebychev polynomial terms for x^(7/5) expanded around x=1.5
+ * Non-zero terms calculated via
+ * NIntegrate[(2/Pi)*ChebyshevT[N,u]/Sqrt [1-u^2]*((u+3)/2)^(7/5),{u,-1,1}, PrecisionGoal->20,
WorkingPrecision -> 100]
+ * Zeroth term is similar except it uses 1/pi rather than 2/pi.
+ */
+static const gdouble iCn[] =
+{
+ 1.7917488588043277509,
+ 0.82045614371976854984,
+ 0.027694100686325412819,
+ -0.00094244335181762134018,
+ 0.000064355540911469709545,
+ -5.7224404636060757485e-6,
+ 5.8767669437311184313e-7,
+ -6.6139920053589721168e-8,
+ 7.9323242696227458163e-9
+};
+
+/* Returns x^(7/5) for x in [1,2) */
+static inline gdouble
+pow75norm (gdouble x)
+{
+ gdouble Tn[9];
+ gdouble u;
+
+ u = 2.0*x - 3.0;
+ Tn[0] = 1.0;
+ Tn[1] = u;
+ Tn[2] = 2*u*Tn[2-1] - Tn[2-2];
+ Tn[3] = 2*u*Tn[3-1] - Tn[3-2];
+ Tn[4] = 2*u*Tn[4-1] - Tn[4-2];
+ Tn[5] = 2*u*Tn[5-1] - Tn[5-2];
+
+ return iCn[0]*Tn[0] + iCn[1]*Tn[1] + iCn[2]*Tn[2] + iCn[3]*Tn[3] + iCn[4]*Tn[4] + iCn[5]*Tn[5];
+}
+
+/* Precalculated (2^N) ^ (7 / 5) */
+static const gdouble pow2_75[5] =
+{
+ 1.0,
+ 2.6390158215457883983,
+ 6.9644045063689921093,
+ 1.8379173679952558018e+1,
+ 4.8502930128332728543e+1
+};
+
+/* Returns x^2.4 == x * x ^1.4 == x * x^(7/5) */
+static inline gdouble
+pow_24 (gdouble x)
+{
+ gdouble s;
+ gint iexp;
+ div_t qr = {0};
+
+ s = frexp (x, &iexp);
+ s *= 2.0;
+ iexp -= 1;
+
+ qr = div (iexp, 5);
+ if (qr.rem < 0)
+ {
+ qr.quot -= 1;
+ qr.rem += 5;
+ }
+
+ return x * ldexp (pow75norm (s) * pow2_75[qr.rem], 7 * qr.quot);
+}
+
+static inline gboolean
+fix_rgb_bounds (GdkRGBA *rgba)
+{
+ gdouble r, g, b;
+ gboolean res = TRUE;
+
+ r = rgba->red;
+ g = rgba->green;
+ b = rgba->blue;
+
+ if (r < 0.0)
+ {
+ rgba->red = 0.0;
+ res = FALSE;
+ }
+ else if (r > 1.0)
+ {
+ rgba->red = 1.0;
+ res = FALSE;
+ }
+
+ if (g < 0.0)
+ {
+ rgba->green = 0.0;
+ res = FALSE;
+ }
+ else if (g > 1.0)
+ {
+ rgba->green = 1.0;
+ res = FALSE;
+ }
+
+ if (b < 0.0)
+ {
+ rgba->blue = 0.0;
+ res = FALSE;
+ }
+ else if (b > 1.0)
+ {
+ rgba->blue = 1.0;
+ res = FALSE;
+ }
+
+ return res;
+}
+
+static inline void
+gstyle_color_convert_rgb_to_srgb (GdkRGBA *rgba,
+ gdouble *red,
+ gdouble *green,
+ gdouble *blue)
+{
+ /* rgba and srgb values range [0, 1] */
+
+ *red = (rgba->red > 0.04045) ? pow_24 (((rgba->red + 0.055) / 1.055)) : rgba->red / 12.92;
+ *green = (rgba->green > 0.04045) ? pow_24 (((rgba->green + 0.055) / 1.055)) : rgba->green / 12.92;
+ *blue = (rgba->blue > 0.04045) ? pow_24 (((rgba->blue + 0.055) / 1.055)) : rgba->blue / 12.92;
+}
+
+static inline void
+gstyle_color_convert_srgb_to_rgb (gdouble red,
+ gdouble green,
+ gdouble blue,
+ GdkRGBA *rgba)
+{
+ /* rgba and srgb values range [0, 1] */
+
+ rgba->red = (red > 0.0031308) ? (pow_1_24 (red) * 1.055) - 0.055 : red * 12.92;
+ rgba->green = (green > 0.0031308) ? (pow_1_24 (green) * 1.055) - 0.055 : green * 12.92;
+ rgba->blue = (blue > 0.0031308) ? (pow_1_24 (blue) * 1.055) - 0.055 : blue * 12.92;
+
+ fix_rgb_bounds (rgba);
+}
+
+static inline void
+gstyle_color_convert_srgb_to_xyz (gdouble red,
+ gdouble green,
+ gdouble blue,
+ GstyleXYZ *xyz)
+{
+ /* srgb range [0, 1] x [0, 0.9505] y [0, 1] z [0, 1.08883] Observer= 2°, Illuminant= D65 */
+
+ xyz->x = (red * 0.4124564 + green * 0.3575761 + blue * 0.1804375);
+ xyz->y = (red * 0.2126729 + green * 0.7151522 + blue * 0.0721750);
+ xyz->z = (red * 0.0193339 + green * 0.1191920 + blue * 0.9503041);
+
+ //fix_xyz_bounds (xyz);
+}
+
+static inline void
+gstyle_color_convert_xyz_to_srgb (GstyleXYZ *xyz,
+ gdouble *red,
+ gdouble *green,
+ gdouble *blue)
+{
+ /* srgb range [0, 1] x [0, 0.9505] y [0, 1] z [0, 1.08883] Observer= 2°, Illuminant= D65 */
+
+ *red = xyz->x * 3.2404542 + xyz->y * -1.5371385 + xyz->z * -0.4985314;
+ *green = xyz->x * -0.9692660 + xyz->y * 1.8760108 + xyz->z * 0.0415560;
+ *blue = xyz->x * 0.0556434 + xyz->y * -0.2040259 + xyz->z * 1.0572252;
+}
+
+inline void
+gstyle_color_convert_cielab_to_xyz (GstyleCielab *lab,
+ GstyleXYZ *xyz)
+{
+ gdouble tmp_x, tmp_y, tmp_z;
+ gdouble pow3_x, pow3_y, pow3_z;
+
+ tmp_y = (lab->l + 16.0 ) / 116.0;
+ tmp_x = lab->a / 500.0 + tmp_y;
+ tmp_z = tmp_y - lab->b / 200.0;
+
+ /* far faster than pow (x, 3) */
+ pow3_x = tmp_x * tmp_x * tmp_x;
+ pow3_y = tmp_y * tmp_y * tmp_y;
+ pow3_z = tmp_z * tmp_z * tmp_z;
+
+ tmp_x = (pow3_x > 0.008856) ? pow3_x : (tmp_x - _16_d_116) / 7.787;
+ tmp_y = (pow3_y > 0.008856) ? pow3_y : (tmp_y - _16_d_116) / 7.787;
+ tmp_z = (pow3_z > 0.008856) ? pow3_z : (tmp_z - _16_d_116) / 7.787;
+
+ xyz->x = tmp_x * D65_xref;
+ xyz->y = tmp_y * D65_yref;
+ xyz->z = tmp_z * D65_zref;
+}
+
+/* fastpow (x, 0.333333333) */
+inline void
+gstyle_color_convert_xyz_to_cielab (GstyleXYZ *xyz,
+ GstyleCielab *lab)
+{
+ /* Observer= 2°, Illuminant= D65 */
+ gdouble x, y, z;
+
+ x = xyz->x / D65_xref;
+ y = xyz->y / D65_yref;
+ z = xyz->z / D65_zref;
+
+ x = (x > 0.008856) ? cbrt (x) : (x * 7.787) + _16_d_116;
+ y = (y > 0.008856) ? cbrt (y) : (y * 7.787) + _16_d_116;
+ z = (z > 0.008856) ? cbrt (z) : (z * 7.787) + _16_d_116;
+
+ lab->l = y * 116.0 - 16.0;
+ lab->a = (x - y) * 500.0;
+ lab->b = (y - z) * 200.0;
+}
+
+/**
+ * gstyle_color_convert_rgb_to_hsl:
+ * @rgba: A #GdkRGBA struct.
+ * @hue: (out): The hue component of a hsl color in range [0.0-360.0[
+ * @saturation: (out): The saturation component of a hsl color in range [0.0-100.0]
+ * @lightness: (out): The lightness component of a hsl color in range [0.0-100.0]
+ *
+ * Convert rgb components to HSL ones.
+ * The alpha component is not used because it doesn't change in the conversion.
+ *
+ */
+void
+gstyle_color_convert_rgb_to_hsl (GdkRGBA *rgba,
+ gdouble *hue,
+ gdouble *saturation,
+ gdouble *lightness)
+{
+ gdouble tmp_hue = 0.0;
+ gdouble tmp_saturation = 0.0;
+ gdouble tmp_lightness;
+
+ gdouble red = rgba->red;
+ gdouble green = rgba->green;
+ gdouble blue = rgba->blue;
+
+ gdouble min;
+ gdouble max;
+ gdouble d;
+ gdouble max_min;
+
+ if (red > green)
+ {
+ max = (red > blue) ? red : blue;
+ min = (green < blue) ? green : blue;
+ }
+ else
+ {
+ max = (green > blue) ? green : blue;
+ min = (red < blue) ? red : blue;
+ }
+
+ max_min = max + min;
+ tmp_lightness = max_min / 2.0;
+ if (max != min)
+ {
+ d = max - min;
+ tmp_saturation = (tmp_lightness > 0.5) ? d / (2.0 - max - min) : d / max_min;
+ if (max == red)
+ tmp_hue = (green - blue) / d + (green < blue ? 6.0 : 0.0);
+ else if (max == green)
+ tmp_hue = (blue - red) / d + 2.0;
+ else
+ tmp_hue = (red - green) / d + 4.0;
+ }
+
+ if (hue != NULL)
+ *hue = tmp_hue * 60.0;
+
+ if (saturation != NULL)
+ *saturation = tmp_saturation * 100.0;
+
+ if (lightness != NULL)
+ *lightness = tmp_lightness * 100.0;
+}
+
+static inline gdouble
+hue2rgb (gdouble m1,
+ gdouble m2,
+ gdouble hue)
+{
+ while (hue < 0.0)
+ hue += 360.0;
+
+ while (hue > 360.0)
+ hue -= 360.0;
+
+ if (hue < 60.0)
+ return m1 + (m2 - m1) * hue / 60.0;
+
+ if (hue < 180.0)
+ return m2;
+
+ if (hue < 240.0)
+ return m1 + (m2 - m1) * (240.0 - hue) / 60.0;
+
+ return m1;
+}
+
+/**
+ * gstyle_color_convert_hsl_to_rgb:
+ * @hue: The hue component of a hsl color in range [0.0-360.0[
+ * @saturation: The saturation component of a hsl color in range [0.0-100.0]
+ * @lightness: The lightness component of a hsl color in range [0.0-100.0]
+ * @rgba: A #GdkRGBA.
+ *
+ * Convert RGB components to HSL ones.
+ * The alpha component is not used because it doesn't change in the conversion.
+ *
+ */
+void
+gstyle_color_convert_hsl_to_rgb (gdouble hue,
+ gdouble saturation,
+ gdouble lightness,
+ GdkRGBA *rgba)
+{
+ gdouble m1;
+ gdouble m2;
+
+ if (saturation == 0.0)
+ rgba->red = rgba->green = rgba->blue = lightness;
+ else
+ {
+ m2 = (lightness > 0.5) ? lightness + saturation - (lightness * saturation) : lightness * (1.0 +
saturation);
+ m1 = 2.0 * lightness - m2;
+
+ rgba->red = hue2rgb (m1, m2, hue + 120.0);
+ rgba->green = hue2rgb (m1, m2, hue);
+ rgba->blue = hue2rgb (m1, m2, hue - 120.0);
+ }
+}
+
+/**
+ * gstyle_color_convert_hsv_to_rgb:
+ * @hue: The hue component of a hsv color in range [0.0-1.0[
+ * @saturation: The saturation component of a hsv color in range [0.0-1.0]
+ * @value: The value component of a hsv color in range [0.0-1.0]
+ * @rgba: A #GdkRGBA.
+ *
+ * Convert HSV components to RGB ones.
+ * The alpha component is not used because it doesn't change in the conversion.
+ *
+ */
+void
+gstyle_color_convert_hsv_to_rgb (gdouble hue,
+ gdouble saturation,
+ gdouble value,
+ GdkRGBA *rgba)
+{
+ gdouble f;
+ gdouble p;
+ gdouble q;
+ gdouble t;
+ gint i;
+
+ if (saturation == 0.0)
+ rgba->red = rgba->green = rgba->blue = value;
+ else
+ {
+ hue *= 6.0;
+ if (hue == 6.0)
+ hue = 0.0;
+
+ i = (int)hue;
+ f = hue - i;
+ p = value * (1.0 - saturation);
+ q = value * (1.0 - saturation * f);
+ t = value * (1.0 - saturation * (1.0 - f));
+
+ switch (i)
+ {
+ case 0:
+ rgba->red = value;
+ rgba->green = t;
+ rgba->blue = p;
+ break;
+
+ case 1:
+ rgba->red = q;
+ rgba->green = value;
+ rgba->blue = p;
+ break;
+
+ case 2:
+ rgba->red = p;
+ rgba->green = value;
+ rgba->blue = t;
+ break;
+
+ case 3:
+ rgba->red = p;
+ rgba->green = q;
+ rgba->blue = value;
+ break;
+
+ case 4:
+ rgba->red = t;
+ rgba->green = p;
+ rgba->blue = value;
+ break;
+
+ case 5:
+ rgba->red = value;
+ rgba->green = p;
+ rgba->blue = q;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+ }
+}
+
+/**
+ * gstyle_color_convert_rgb_to_xyz:
+ * @rgba: An #GdkRGBA.
+ * @xyz: A #GstyleXYZ.
+ *
+ * Convert RGB components to XYZ ones.
+ * The alpha component is not used because it doesn't change in the conversion.
+ *
+ */
+void
+gstyle_color_convert_rgb_to_xyz (GdkRGBA *rgba,
+ GstyleXYZ *xyz)
+{
+ gdouble srgb_red, srgb_green, srgb_blue;
+
+ gstyle_color_convert_rgb_to_srgb (rgba, &srgb_red, &srgb_green, &srgb_blue);
+ gstyle_color_convert_srgb_to_xyz (srgb_red, srgb_green, srgb_blue, xyz);
+}
+
+/**
+ * gstyle_color_convert_rgb_to_hsv:
+ * @rgba: An #GdkRGBA.
+ * @hue: (out): The hue component of a hsv color in range [0.0-1.0[
+ * @saturation: (out): The saturation component of a hsv color in range [0.0-1.0]
+ * @value: (out): The value component of a hsv color in range [0.0-1.0]
+ *
+ * Convert RGB components to HSV ones.
+ * The alpha component is not used because it doesn't change in the conversion.
+ *
+ */
+void
+gstyle_color_convert_rgb_to_hsv (GdkRGBA *rgba,
+ gdouble *hue,
+ gdouble *saturation,
+ gdouble *value)
+{
+ gdouble vmin, vmax, delta;
+ gdouble d_red, d_green, d_blue;
+ gdouble delta_d_2;
+
+ if (rgba->red > rgba->green)
+ {
+ vmax = (rgba->red > rgba->blue) ? rgba->red : rgba->blue;
+ vmin = (rgba->green < rgba->blue) ? rgba->green : rgba->blue;
+ }
+ else
+ {
+ vmax = (rgba->green > rgba->blue) ? rgba->green : rgba->blue;
+ vmin = (rgba->red < rgba->blue) ? rgba->red : rgba->blue;
+ }
+
+ delta = vmax - vmin;
+ delta_d_2 = delta / 2.0;
+
+ *value = vmax;
+
+ if (delta < 1e-20 )
+ *hue = *saturation = 0.0;
+ else
+ {
+ *saturation = delta / vmax;
+
+ d_red = ((vmax - rgba->red) / 6.0 + delta_d_2) / delta;
+ d_green = ((vmax - rgba->green) / 6.0 + delta_d_2) / delta;
+ d_blue = ((vmax - rgba->blue) / 6.0 + delta_d_2) / delta;
+
+ if (vmax == rgba->red)
+ *hue = d_blue - d_green;
+ else if (vmax == rgba->green)
+ *hue = one_third + d_red - d_blue;
+ else if (vmax == rgba->blue)
+ *hue = two_third + d_green - d_red;
+
+ if (*hue < 0.0)
+ *hue += 1.0;
+ else if (*hue > 1.0 )
+ *hue -= 1.0;
+ }
+}
+
+/**
+ * gstyle_color_convert_rgb_to_cielab:
+ * @rgba: An #GdkRGBA.
+ * @lab: (out): A #GstyleCieLab struct.
+ *
+ * Convert RGB components to CIELAB ones.
+ * The alpha component is not used because it doesn't change in the conversion.
+ *
+ */
+void
+gstyle_color_convert_rgb_to_cielab (GdkRGBA *rgba,
+ GstyleCielab *lab)
+{
+ gdouble srgb_red, srgb_green, srgb_blue;
+ GstyleXYZ xyz;
+
+ gstyle_color_convert_rgb_to_srgb (rgba, &srgb_red, &srgb_green, &srgb_blue);
+ gstyle_color_convert_srgb_to_xyz (srgb_red, srgb_green, srgb_blue, &xyz);
+ gstyle_color_convert_xyz_to_cielab (&xyz, lab);
+}
+
+/**
+ * gstyle_color_convert_cielab_to_rgb:
+ * @lab: A #GstyleCieLab struct.
+ * @rgba: (out): An #GdkRGBA.
+ *
+ * Convert CIELAB components to RGB ones.
+ * The alpha component is not used because it doesn't change in the conversion.
+ *
+ */
+void
+gstyle_color_convert_cielab_to_rgb (GstyleCielab *lab,
+ GdkRGBA *rgba)
+{
+ gdouble srgb_red, srgb_green, srgb_blue;
+ GstyleXYZ xyz;
+
+ gstyle_color_convert_cielab_to_xyz (lab, &xyz);
+ gstyle_color_convert_xyz_to_srgb (&xyz, &srgb_red, &srgb_green, &srgb_blue);
+ gstyle_color_convert_srgb_to_rgb (srgb_red, srgb_green, srgb_blue, rgba);
+}
+
+inline void
+gstyle_color_convert_xyz_to_rgb (GstyleXYZ *xyz,
+ GdkRGBA *rgba)
+{
+ gdouble srgb_red, srgb_green, srgb_blue;
+
+ gstyle_color_convert_xyz_to_srgb (xyz, &srgb_red, &srgb_green, &srgb_blue);
+ gstyle_color_convert_srgb_to_rgb (srgb_red, srgb_green, srgb_blue, rgba);
+}
+
+/**
+ * gstyle_color_convert_hsv_to_xyz:
+ * @hue: The hue component of a hsv color in range [0.0-1.0[
+ * @saturation: The saturation component of a hsv color in range [0.0-1.0]
+ * @value: The value component of a hsv color in range [0.0-1.0]
+ * @xyz: (out): An #GstyleXYZ.
+ *
+ * Convert HSV components to XYZ ones.
+ *
+ */
+void
+gstyle_color_convert_hsv_to_xyz (gdouble hue,
+ gdouble saturation,
+ gdouble value,
+ GstyleXYZ *xyz)
+{
+ gdouble srgb_red, srgb_green, srgb_blue;
+ GdkRGBA rgba;
+
+ gstyle_color_convert_hsv_to_rgb (hue, saturation, value, &rgba);
+ gstyle_color_convert_rgb_to_srgb (&rgba, &srgb_red, &srgb_green, &srgb_blue);
+ gstyle_color_convert_srgb_to_xyz (srgb_red, srgb_green, srgb_blue, xyz);
+}
+
+/**
+ * gstyle_color_convert_xyz_to_hsv:
+ * @xyz: An #GstyleXYZ.
+ * @hue: (out): The hue component of a hsv color in range [0.0-1.0[
+ * @saturation: (out): The saturation component of a hsv color in range [0.0-1.0]
+ * @value: (out): The value component of a hsv color in range [0.0-1.0]
+ *
+ * Convert XYZ components to HSV ones.
+ *
+ */
+void
+gstyle_color_convert_xyz_to_hsv (GstyleXYZ *xyz,
+ gdouble *hue,
+ gdouble *saturation,
+ gdouble *value)
+{
+ GdkRGBA rgba;
+ gdouble srgb_red, srgb_green, srgb_blue;
+
+ gstyle_color_convert_xyz_to_srgb (xyz, &srgb_red, &srgb_green, &srgb_blue);
+ gstyle_color_convert_srgb_to_rgb (srgb_red, srgb_green, srgb_blue, &rgba);
+ gstyle_color_convert_rgb_to_hsv (&rgba, hue, saturation, value);
+}
+
+/**
+ * gstyle_color_delta_e:
+ * @lab1: A #GstyleCielab.
+ * @lab2: A #GstyleCielab.
+ *
+ * Compute the color difference between lab1 and lab2,
+ * based on the deltaE CIEDE2000 formula.
+ *
+ * Returns: the deltaE value.
+ *
+ */
+gdouble
+gstyle_color_delta_e (GstyleCielab *lab1,
+ GstyleCielab *lab2)
+{
+ gdouble ap1, Cp1, hp1, Cab1;
+ gdouble ap2, Cp2, hp2, Cab2;
+ gdouble Cab;
+
+ gdouble Cp1Cp2;
+ gdouble Cab_pow_7, G;
+
+ gdouble DLp, DCp, DHp;
+ gdouble dhp, Lp, Cp, hp;
+
+ gdouble T;
+ gdouble Dtheta_rad;
+ gdouble Rc, RT;
+
+ gdouble _50Lp_pow2;
+ gdouble SL, SC, SH;
+
+ gdouble lab1_bb = lab1->b * lab1->b;
+ gdouble lab2_bb = lab2->b * lab2->b;
+
+ Cab1 = sqrt (lab1->a * lab1->a + lab1_bb);
+ Cab2 = sqrt (lab2->a * lab2->a + lab2_bb);
+ Cab = (Cab1 + Cab2) / 2.0;
+ Cab_pow_7 = pow (Cab, 7);
+
+ G = 0.5 * (1.0 - sqrt (Cab_pow_7 / (Cab_pow_7 + _25_pow_7)));
+
+ ap1 = (1.0 + G) * lab1->a;
+ ap2 = (1.0 + G) * lab2->a;
+
+ Cp1 = sqrt (ap1 * ap1 + lab1_bb);
+ Cp2 = sqrt (ap2 * ap2 + lab2_bb);
+ Cp1Cp2 = (Cp1 * Cp2);
+
+ if (ap1 == 0 && lab1->b == 0)
+ hp1 = 0.0;
+ else
+ {
+ hp1 = atan2 (lab1->b, ap1);
+ if (hp1 < 0)
+ hp1 += _2PI;
+ }
+
+ if (ap2 == 0 && lab2->b == 0)
+ hp2 = 0.0;
+ else
+ {
+ hp2 = atan2 (lab2->b, ap2);
+ if (hp2 < 0)
+ hp2 += _2PI;
+ }
+
+ DLp = (lab2->l - lab1->l);
+ DCp = (Cp2 - Cp1);
+
+ if (Cp1Cp2 == 0.0)
+ {
+ dhp = 0.0;
+ DHp = 0.0;
+
+ hp = hp1 + hp2;
+ }
+ else
+ {
+ dhp = (hp2 - hp1);
+ if (dhp > G_PI)
+ dhp -= _2PI;
+
+ if (dhp < -G_PI)
+ dhp += _2PI;
+
+ DHp = 2.0 * sqrt (Cp1Cp2) * sin (dhp / 2.0);
+
+ hp = (hp1 + hp2) / 2.0;
+ if (fabs (hp1 - hp2) > G_PI)
+ hp -= G_PI;
+
+ if (hp < 0)
+ hp += _2PI;
+ }
+
+ Lp = (lab1->l + lab2->l) / 2.0;
+ Cp = (Cp1 + Cp2) / 2.0;
+
+ T = 1.0 - 0.17 * cos (hp - PI_d_6) +
+ 0.24 * cos (2.0 * hp) +
+ 0.32 * cos (3.0 * hp + PI_d_30) -
+ 0.20 * cos (4.0 * hp - _63PI_d_180);
+
+ Dtheta_rad = _30PI_d_180 * exp (-pow (((_180_d_PI * hp - 275.0) / 25.0), 2.0));
+
+ Rc = 2.0 * sqrt (pow (Cp, 7.0) / (pow (Cp, 7.0) + _25_pow_7));
+
+ _50Lp_pow2 = (Lp - 50.0) * (Lp - 50.0);
+ SL = 1.0 + (0.015 * _50Lp_pow2 / sqrt (20.0 + _50Lp_pow2));
+ SC = 1.0 + 0.045 * Cp;
+ SH = 1.0 + 0.015 * Cp * T;
+
+ RT = -sin (2.0 * Dtheta_rad) * Rc;
+
+ return sqrt( pow ((DLp / SL), 2.0) +
+ pow((DCp / SC), 2.0) +
+ pow((DHp / SH), 2.0) +
+ RT * (DCp / SC) * (DHp / SH));
+}
diff --git a/contrib/gstyle/gstyle-color-convert.h b/contrib/gstyle/gstyle-color-convert.h
new file mode 100644
index 0000000..7f2071a
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-convert.h
@@ -0,0 +1,75 @@
+/* gstyle-color-convert.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_CONVERT_H
+#define GSTYLE_COLOR_CONVERT_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "gstyle-types.h"
+#include "gstyle-cielab.h"
+#include "gstyle-hsv.h"
+#include "gstyle-xyz.h"
+
+G_BEGIN_DECLS
+
+void gstyle_color_convert_rgb_to_hsl (GdkRGBA *rgba,
+ gdouble *hue,
+ gdouble *saturation,
+ gdouble *lightness);
+void gstyle_color_convert_rgb_to_hsv (GdkRGBA *rgba,
+ gdouble *hue,
+ gdouble *saturation,
+ gdouble *value);
+void gstyle_color_convert_hsl_to_rgb (gdouble hue,
+ gdouble saturation,
+ gdouble lightness,
+ GdkRGBA *rgba);
+void gstyle_color_convert_hsv_to_rgb (gdouble hue,
+ gdouble saturation,
+ gdouble value,
+ GdkRGBA *rgba);
+void gstyle_color_convert_rgb_to_cielab (GdkRGBA *rgba,
+ GstyleCielab *lab);
+void gstyle_color_convert_cielab_to_rgb (GstyleCielab *lab,
+ GdkRGBA *rgba);
+gdouble gstyle_color_delta_e (GstyleCielab *lab1,
+ GstyleCielab *lab2);
+
+void gstyle_color_convert_rgb_to_xyz (GdkRGBA *rgba,
+ GstyleXYZ *xyz);
+extern void gstyle_color_convert_cielab_to_xyz (GstyleCielab *lab,
+ GstyleXYZ *xyz);
+void gstyle_color_convert_hsv_to_xyz (gdouble hue,
+ gdouble saturation,
+ gdouble value,
+ GstyleXYZ *xyz);
+
+extern void gstyle_color_convert_xyz_to_cielab (GstyleXYZ *xyz,
+ GstyleCielab *lab);
+extern void gstyle_color_convert_xyz_to_rgb (GstyleXYZ *xyz,
+ GdkRGBA *rgba);
+void gstyle_color_convert_xyz_to_hsv (GstyleXYZ *xyz,
+ gdouble *hue,
+ gdouble *saturation,
+ gdouble *value);
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_CONVERT_H */
diff --git a/contrib/gstyle/gstyle-color-filter.c b/contrib/gstyle/gstyle-color-filter.c
new file mode 100644
index 0000000..a264ee0
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-filter.c
@@ -0,0 +1,42 @@
+/* gstyle-color-filter.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gstyle-color-filter.h"
+
+static gdouble web_colors [] = {0.0, 0.2, 0.2, 0.4, 0.4 , 0.6 , 0.6, 0.8, 0.8, 1.0, 1.0};
+#define TO_WEB_COLOR(x) (web_colors [(gint)(x * 10.0)])
+
+/**
+ * gstyle_color_filter_func:
+ * @rgba: The source #GdkRGBA color
+ * @filter_rgba: (out): the filtered #GdkRGBA color
+ * @user_data: A user data pointer
+ *
+ * A WebSafe Color filter usable with #GstyleColorScale and GstyleColorPlane.
+ *
+ */
+void
+gstyle_color_filter_websafe (GdkRGBA *rgba,
+ GdkRGBA *filter_rgba,
+ gpointer user_data)
+{
+ filter_rgba->red = TO_WEB_COLOR (rgba->red);
+ filter_rgba->green = TO_WEB_COLOR (rgba->green);
+ filter_rgba->blue = TO_WEB_COLOR (rgba->blue);
+ filter_rgba->alpha = rgba->alpha;
+}
diff --git a/contrib/gstyle/gstyle-color-filter.h b/contrib/gstyle/gstyle-color-filter.h
new file mode 100644
index 0000000..085bd5a
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-filter.h
@@ -0,0 +1,36 @@
+/* gstyle-color-filter.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_FILTER_H
+#define GSTYLE_COLOR_FILTER_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+typedef void (*GstyleColorFilter)(GdkRGBA *rgba, GdkRGBA *filter_rgba, gpointer user_data);
+
+void gstyle_color_filter_websafe (GdkRGBA *rgba,
+ GdkRGBA *filter_rgba,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_FILTER_H */
+
diff --git a/contrib/gstyle/gstyle-color-item.c b/contrib/gstyle/gstyle-color-item.c
new file mode 100644
index 0000000..36fa795
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-item.c
@@ -0,0 +1,209 @@
+/* gstyle-color-item.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-color-item"
+
+#include "gstyle-color-item.h"
+
+G_DEFINE_BOXED_TYPE (GstyleColorItem, gstyle_color_item, gstyle_color_item_ref, gstyle_color_item_unref)
+
+/**
+ * gstyle_color_item_get_color:
+ * @self: A #GstyleColorItem
+ *
+ * Get the #GstyleColor inside the #GstyleColorItem.
+ *
+ * Returns: (transfer none): A #GstyleColor.
+ *
+ */
+const GstyleColor *
+gstyle_color_item_get_color (GstyleColorItem *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ return self->color;
+}
+
+/**
+ * gstyle_color_item_set_color:
+ * @self: A #GstyleColorItem
+ * @color: (nullable): A #GstyleColor or %NULL
+ *
+ * Set the #GstyleColor inside the #GstyleColorItem.
+ *
+ */
+void
+gstyle_color_item_set_color (GstyleColorItem *self,
+ GstyleColor *color)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (GSTYLE_IS_COLOR (color));
+
+ g_clear_object (&self->color);
+ self->color = g_object_ref (color);
+}
+
+/**
+ * gstyle_color_item_get_start:
+ * @self: A #GstyleColorItem
+ *
+ * Get the start position of the #GstyleColorItem.
+ *
+ * Returns: a position in bytes, in the analysed buffer, starting from offset 0.
+ *
+ */
+guint
+gstyle_color_item_get_start (GstyleColorItem *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+
+ return self->start;
+}
+
+/**
+ * gstyle_color_item_get_len:
+ * @self: A #GstyleColorItem
+ *
+ * Get the size of the #GstyleColorItem.
+ *
+ * Returns: the #GstyleColorItem size in bytes.
+ *
+ */
+guint
+gstyle_color_item_get_len (GstyleColorItem *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+
+ return self->len;
+}
+
+/**
+ * gstyle_color_item_copy:
+ * @self: a #GstyleColorItem
+ *
+ * Makes a deep copy of a #GstyleColorItem
+ *
+ * The result must be freed with gstyle_color_item_free()
+ * or un-refed with gstyle_color_item_unref().
+ *
+ * Returns: (transfer full): A newly allocated #GstyleColorItem, with the same contents as @self
+ *
+ */
+GstyleColorItem *
+gstyle_color_item_copy (GstyleColorItem *self)
+{
+ GstyleColorItem *item;
+ GstyleColor *src_color;
+
+ g_return_val_if_fail (self != NULL, NULL);
+
+ item = g_slice_dup (GstyleColorItem, self);
+
+ src_color = (GstyleColor *)gstyle_color_item_get_color (self);
+ if (src_color != NULL && GSTYLE_IS_COLOR (src_color))
+ self->color = gstyle_color_copy (src_color);
+
+ return item;
+}
+
+/**
+ * gstyle_color_item_free:
+ * @self: a #GstyleColorItem
+ *
+ * Free a #GstyleColorItem created with gstyle_color_item_copy().
+ *
+ */
+void
+gstyle_color_item_free (GstyleColorItem *self)
+{
+ g_return_if_fail (self != NULL);
+ g_assert_cmpint (self->ref_count, ==, 0);
+
+ if (self->color != NULL && GSTYLE_IS_COLOR (self->color))
+ g_object_unref (self->color);
+
+ g_slice_free (GstyleColorItem, self);
+}
+
+/**
+ * gstyle_color_item_new:
+ * @color: (nullable): A #GstyleColor or NULL
+ * @start: start offset of the item
+ * @len: length of the item
+ *
+ * Return a new #GstyleColorItem.
+ * It must be freed with gstyle_color_item_free().
+ *
+ * Returns: a #GstyleColorItem.
+ *
+ */
+GstyleColorItem *
+gstyle_color_item_new (GstyleColor *color,
+ gint start,
+ gint len)
+{
+ GstyleColorItem *item;
+
+ g_return_val_if_fail (GSTYLE_IS_COLOR (color) || color == NULL, NULL);
+
+ item = g_slice_new0 (GstyleColorItem);
+ item->ref_count = 1;
+ item->start = start;
+ item->len = len;
+
+ if (color != NULL)
+ item->color = g_object_ref (color);
+
+ return item;
+}
+
+/**
+ * gstyle_color_item_ref:
+ * @self: An #GstyleColorItem
+ *
+ * Increments the reference count of @self by one.
+ *
+ * Returns: (transfer none): @self
+ */
+GstyleColorItem *
+gstyle_color_item_ref (GstyleColorItem *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (self->ref_count, NULL);
+
+ g_atomic_int_inc (&self->ref_count);
+
+ return self;
+}
+
+/**
+ * gstyle_color_item_unref:
+ * @self: (transfer none): An #GstyleColorItem
+ *
+ * Decrements the reference count of @GstyleColorItem by one, freeing the structure
+ * when the reference count reaches zero.
+ */
+void
+gstyle_color_item_unref (GstyleColorItem *self)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (self->ref_count);
+
+ if (g_atomic_int_dec_and_test (&self->ref_count))
+ gstyle_color_item_free (self);
+}
diff --git a/contrib/gstyle/gstyle-color-item.h b/contrib/gstyle/gstyle-color-item.h
new file mode 100644
index 0000000..20a12d9
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-item.h
@@ -0,0 +1,58 @@
+/* gstyle-color-item.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_ITEM_H
+#define GSTYLE_COLOR_ITEM_H
+
+#include <glib.h>
+
+#include "gstyle-types.h"
+#include "gstyle-color.h"
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_COLOR_ITEM (gstyle_color_item_get_type ())
+
+struct _GstyleColorItem
+{
+ GstyleColor *color;
+ guint start;
+ guint len;
+ volatile gint ref_count;
+};
+
+GType gstyle_color_item_get_type (void) G_GNUC_CONST;
+
+GstyleColorItem *gstyle_color_item_new (GstyleColor *color,
+ gint start,
+ gint len);
+GstyleColorItem *gstyle_color_item_copy (GstyleColorItem *self);
+void gstyle_color_item_free (GstyleColorItem *self);
+GstyleColorItem *gstyle_color_item_ref (GstyleColorItem *self);
+void gstyle_color_item_unref (GstyleColorItem *self);
+
+const GstyleColor *gstyle_color_item_get_color (GstyleColorItem *self);
+void gstyle_color_item_set_color (GstyleColorItem *self,
+ GstyleColor *color);
+
+guint gstyle_color_item_get_start (GstyleColorItem *self);
+guint gstyle_color_item_get_len (GstyleColorItem *self);
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_ITEM_H */
diff --git a/contrib/gstyle/gstyle-color-panel-actions.c b/contrib/gstyle/gstyle-color-panel-actions.c
new file mode 100644
index 0000000..53cd501
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-panel-actions.c
@@ -0,0 +1,84 @@
+/* gstyle-color-panel-actions.c
+ *
+ * Copyright (C) 2016 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-color-panel"
+
+#include "gstyle-color-panel-private.h"
+#include "gstyle-color-panel.h"
+#include "gstyle-slidein.h"
+
+#include "gstyle-color-panel-actions.h"
+
+static void
+gstyle_color_panel_actions_toggle_page (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ GstyleColorPanel *self = (GstyleColorPanel *)user_data;
+ g_autoptr (GVariant) value = NULL;
+ g_autofree gchar *page_name = NULL;
+ const gchar *name;
+ gboolean state;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ value = g_action_get_state (G_ACTION (action));
+ state = g_variant_get_boolean (value);
+ name = g_action_get_name(G_ACTION (action));
+ if (!g_str_has_prefix (name, "toggle-"))
+ return;
+
+ page_name = g_strdup (&name [7]);
+ g_simple_action_set_state (action, g_variant_new_boolean (!state));
+ if (!state)
+ {
+ _gstyle_color_panel_update_prefs_page (self, page_name);
+ gtk_stack_set_visible_child_name (self->prefs_stack, page_name);
+ }
+
+ gstyle_slidein_reveal_slide (GSTYLE_SLIDEIN (self->prefs_slidein),
+ !gstyle_slidein_get_revealed (GSTYLE_SLIDEIN (self->prefs_slidein)));
+}
+
+static GActionEntry ColorPanelPagesPrefsActions[] = {
+ { "toggle-components-page", NULL, "b", "false", gstyle_color_panel_actions_toggle_page },
+ { "toggle-colorstrings-page", NULL, "b", "false", gstyle_color_panel_actions_toggle_page },
+ { "toggle-palettes-page", NULL, "b", "false", gstyle_color_panel_actions_toggle_page },
+ { "toggle-paletteslist-page", NULL, "b", "false", gstyle_color_panel_actions_toggle_page },
+};
+
+void
+gstyle_color_panel_actions_init (GstyleColorPanel *self)
+{
+ g_autoptr (GSimpleActionGroup) pages_group = NULL;
+ GActionGroup *palette_widget_actions_group;
+
+ pages_group = g_simple_action_group_new ();
+ g_action_map_add_action_entries (G_ACTION_MAP (pages_group), ColorPanelPagesPrefsActions,
+ G_N_ELEMENTS (ColorPanelPagesPrefsActions), self);
+ gtk_widget_insert_action_group (GTK_WIDGET (self), "gstyle-pages-prefs", G_ACTION_GROUP (pages_group));
+
+ if (self->palette_widget != NULL)
+ {
+ palette_widget_actions_group = gtk_widget_get_action_group (GTK_WIDGET (self->palette_widget),
"gstyle-palettes-prefs");
+ if (palette_widget_actions_group != NULL)
+ gtk_widget_insert_action_group (GTK_WIDGET (self),
+ "gstyle-palettes-prefs",
+ palette_widget_actions_group);
+ }
+}
diff --git a/contrib/gstyle/gstyle-color-panel-actions.h b/contrib/gstyle/gstyle-color-panel-actions.h
new file mode 100644
index 0000000..9a59593
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-panel-actions.h
@@ -0,0 +1,32 @@
+/* gstyle-color-panel-actions.h
+ *
+ * Copyright (C) 2016 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GSTYLE_COLOR_PANEL_ACTIONS_H
+#define GSTYLE_COLOR_PANEL_ACTIONS_H
+
+#include <glib-object.h>
+
+#include "gstyle-color-panel.h"
+
+G_BEGIN_DECLS
+
+void gstyle_color_panel_actions_init (GstyleColorPanel *self);
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_PANEL_ACTIONS_H */
+
diff --git a/contrib/gstyle/gstyle-color-panel-private.h b/contrib/gstyle/gstyle-color-panel-private.h
new file mode 100644
index 0000000..1b70997
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-panel-private.h
@@ -0,0 +1,151 @@
+/* gstyle-color-panel-private.h
+ *
+ * Copyright (C) 2016 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_PANEL_PRIVATE_H
+#define GSTYLE_COLOR_PANEL_PRIVATE_H
+
+#include <glib-object.h>
+
+#include <fuzzy.h>
+
+#include "gstyle-cielab.h"
+#include "gstyle-color.h"
+#include "gstyle-color-convert.h"
+#include "gstyle-color-component.h"
+#include "gstyle-color-plane.h"
+#include "gstyle-color-scale.h"
+#include "gstyle-color-widget.h"
+#include "gstyle-css-provider.h"
+#include "gstyle-eyedropper.h"
+#include "gstyle-palette-widget.h"
+#include "gstyle-revealer.h"
+#include "gstyle-slidein.h"
+#include "gstyle-utils.h"
+
+#include "gstyle-color-panel.h"
+
+G_BEGIN_DECLS
+
+#define PREFS_COMPONENTS_PAGE "components-page"
+#define PREFS_COLOR_STRINGS_PAGE "colorstrings-page"
+#define PREFS_PALETTES_PAGE "palettes-page"
+#define PREFS_PALETTES_LIST_PAGE "paletteslist-page"
+
+typedef struct _ColorComp
+{
+ GtkToggleButton *toggle;
+ GtkSpinButton *spin;
+ GstyleColorScale *scale;
+
+ gulong toggle_handler_id;
+} ColorComp;
+
+struct _GstyleColorPanel
+{
+ GtkBox parent_instance;
+
+ GstyleCssProvider *default_provider;
+
+ GstyleColorPlane *color_plane;
+ GtkAdjustment *adj_alpha;
+
+ GstyleColor *new_color;
+ GstyleColor *old_color;
+ GstyleColorWidget *new_swatch;
+ GstyleColorWidget *old_swatch;
+
+ GtkButton *picker_button;
+ GstyleEyedropper *eyedropper;
+ GtkWidget *search_color_entry;
+ GtkWidget *search_strings_popover;
+ GtkWidget *search_strings_list;
+
+ Fuzzy *fuzzy;
+
+ GtkToggleButton *components_toggle;
+ GtkToggleButton *strings_toggle;
+ GtkToggleButton *palette_toggle;
+
+ GtkWidget *hsv_grid;
+ GtkWidget *lab_grid;
+ GtkWidget *rgb_grid;
+
+ GstyleRevealer *scale_reveal;
+ GstyleRevealer *string_reveal;
+ GstyleRevealer *palette_reveal;
+
+ GtkWidget *components_controls;
+ GtkWidget *strings_controls;
+ GtkWidget *palette_controls;
+
+ GstyleColorScale *ref_scale;
+ GstyleColorScale *alpha_scale;
+
+ GtkLabel *res_hex3_label;
+ GtkLabel *res_hex6_label;
+ GtkLabel *res_rgb_label;
+ GtkLabel *res_rgba_label;
+ GtkLabel *res_hsl_label;
+ GtkLabel *res_hsla_label;
+
+ GtkLabel *hex3_label;
+ GtkLabel *hex6_label;
+ GtkLabel *rgb_label;
+ GtkLabel *rgba_label;
+ GtkLabel *hsl_label;
+ GtkLabel *hsla_label;
+
+ GstylePaletteWidget *palette_widget;
+
+ GIcon *degree_icon;
+ GIcon *percent_icon;
+
+ GtkToggleButton *components_prefs_button;
+ GtkToggleButton *color_strings_prefs_button;
+ GtkToggleButton *palettes_prefs_button;
+ GtkToggleButton *palettes_list_prefs_button;
+ GtkToggleButton *last_checked_prefs_button;
+
+ gulong components_prefs_button_handler_id;
+ gulong color_strings_prefs_button_handler_id;
+ gulong palettes_prefs_button_handler_id;
+ gulong palettes_list_prefs_button_handler_id;
+
+ GtkWidget *components_prefs_bin;
+ GtkWidget *color_strings_prefs_bin;
+ GtkWidget *palettes_prefs_bin;
+ GtkWidget *palettes_list_prefs_bin;
+
+ ColorComp components [N_GSTYLE_COLOR_COMPONENT];
+ GstyleColorComponent current_comp;
+ GstyleColorUnit preferred_unit;
+
+ GstyleSlidein *prefs_slidein;
+ GtkStack *prefs_stack;
+ GtkWidget *last_toggled_prefs;
+
+ GstyleColorPanelStringsVisibleFlags strings_visible_flags;
+};
+
+void _gstyle_color_panel_update_prefs_page (GstyleColorPanel *self,
+ const gchar
*page_name);
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_PANEL_PRIVATE_H */
+
diff --git a/contrib/gstyle/gstyle-color-panel.c b/contrib/gstyle/gstyle-color-panel.c
new file mode 100644
index 0000000..7832d41
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-panel.c
@@ -0,0 +1,1465 @@
+/* gstyle-color-panel.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-color-panel"
+
+#include <glib/gi18n.h>
+
+#include "gstyle-color-panel-private.h"
+#include "gstyle-color-panel-actions.h"
+#include "gstyle-revealer.h"
+#include "gstyle-color.h"
+
+#define HSV_TO_SCALE_FACTOR (1.0 / 256.0)
+#define CIELAB_L_TO_SCALE_FACTOR (100.0 / 256.0)
+
+#define PREFS_BOX_MARGIN (10)
+
+G_DEFINE_TYPE (GstyleColorPanel, gstyle_color_panel, GTK_TYPE_BOX)
+
+static gchar * comp_names [N_GSTYLE_COLOR_COMPONENT] =
+ {
+ "hsv_h",
+ "hsv_s",
+ "hsv_v",
+ "lab_l",
+ "lab_a",
+ "lab_b",
+ "rgb_red",
+ "rgb_green",
+ "rgb_blue"
+ };
+
+/* Conversion between component and color plane mode :
+ * component without a corresponding mode return
+ * GSTYLE_COLOR_PLANE_MODE_NONE
+ */
+static GstyleColorPlaneMode component_to_plane_mode [N_GSTYLE_COLOR_COMPONENT] =
+ {
+ GSTYLE_COLOR_PLANE_MODE_HUE,
+ GSTYLE_COLOR_PLANE_MODE_SATURATION,
+ GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS,
+ GSTYLE_COLOR_PLANE_MODE_CIELAB_L,
+ GSTYLE_COLOR_PLANE_MODE_CIELAB_A,
+ GSTYLE_COLOR_PLANE_MODE_CIELAB_B,
+ GSTYLE_COLOR_PLANE_MODE_RED,
+ GSTYLE_COLOR_PLANE_MODE_GREEN,
+ GSTYLE_COLOR_PLANE_MODE_BLUE
+ };
+
+enum {
+ PROP_0,
+ PROP_HSV_VISIBLE,
+ PROP_LAB_VISIBLE,
+ PROP_RGB_VISIBLE,
+ PROP_RGB_UNIT,
+ PROP_STRINGS_VISIBLE,
+ PROP_RGBA,
+ PROP_XYZ,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+enum {
+ UPDATE_PREFS,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL];
+
+/**
+ * gstyle_color_panel_get_rgba:
+ * @self: A #GstyleColorPanel.
+ * @rgba: (out): A #GdkRGBA adress.
+ *
+ * Fill @rgba with the current color plane rgba.
+ *
+ */
+void
+gstyle_color_panel_get_rgba (GstyleColorPanel *self,
+ GdkRGBA *rgba)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_PANEL (self));
+
+ gstyle_color_plane_get_rgba (self->color_plane, rgba);
+ rgba->alpha = gtk_adjustment_get_value (self->adj_alpha) / 100.0;
+}
+
+/**
+ * gstyle_color_panel_get_xyz:
+ * @self: A #GstyleColorPanel.
+ * @xyz: (out): A #GstyleXYZ adress.
+ *
+ * Fill @xyz with the current color plane xyz.
+ *
+ */
+void
+gstyle_color_panel_get_xyz (GstyleColorPanel *self,
+ GstyleXYZ *xyz)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_PANEL (self));
+
+ gstyle_color_plane_get_xyz (self->color_plane, xyz);
+ xyz->alpha = gtk_adjustment_get_value (self->adj_alpha) / 100.0;
+}
+
+/**
+ * gstyle_color_panel_set_rgba:
+ * @self: A #GstyleColorPanel.
+ * @rgba: A #GdkRGBA.
+ *
+ * Set the color plane and sliders to rgba.
+ *
+ */
+void
+gstyle_color_panel_set_rgba (GstyleColorPanel *self,
+ const GdkRGBA *rgba)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_PANEL (self));
+
+ gtk_adjustment_set_value (self->adj_alpha, rgba->alpha * 100.0);
+ gstyle_color_plane_set_rgba (self->color_plane, rgba);
+}
+
+/**
+ * gstyle_color_panel_set_xyz:
+ * @self: A #GstyleColorPanel.
+ * @xyz: A #GstyleXYZ.
+ *
+ * Set the color plane and sliders to the xyz value.
+ *
+ */
+void
+gstyle_color_panel_set_xyz (GstyleColorPanel *self,
+ const GstyleXYZ *xyz)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_PANEL (self));
+
+ gtk_adjustment_set_value (self->adj_alpha, xyz->alpha * 100.0);
+ gstyle_color_plane_set_xyz (self->color_plane, xyz);
+}
+
+/**
+ * gstyle_color_panel_get_palette_widget:
+ * @self: A #GstyleColorPanel.
+ *
+ * Returns: (nullable) (transfer none): The #GstylePaletteWidget used by the panel.
+ *
+ */
+GstylePaletteWidget *
+gstyle_color_panel_get_palette_widget (GstyleColorPanel *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR_PANEL (self), NULL);
+
+ return self->palette_widget;
+}
+
+static void
+update_hsv_hue_color_ramp (GstyleColorPanel *self,
+ GstyleColorScale *scale,
+ GdkRGBA *rgba)
+{
+ gdouble hue;
+ guint32 *data;
+ GdkRGBA dst_rgba = {0};
+
+ /* TODO: malloc in init and keep data around */
+ data = g_malloc0 (GSTYLE_COLOR_SCALE_CUSTOM_DATA_BYTE_SIZE);
+ for (gint x = 0; x < GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE; ++x)
+ {
+ hue = x * HSV_TO_SCALE_FACTOR;
+ gstyle_color_convert_hsv_to_rgb (hue, 1.0, 1.0, &dst_rgba);
+ data [x] = pack_rgba24 (&dst_rgba);
+ }
+
+ gstyle_color_scale_set_custom_data (scale, data);
+ g_free (data);
+}
+
+static void
+update_hsv_saturation_color_ramp (GstyleColorPanel *self,
+ GstyleColorScale *scale,
+ GdkRGBA *rgba)
+{
+ gdouble hue, saturation, value;
+ guint32 *data;
+ GdkRGBA dst_rgba = {0};
+
+ /* TODO: malloc in init and keep data around */
+ data = g_malloc0 (GSTYLE_COLOR_SCALE_CUSTOM_DATA_BYTE_SIZE);
+ gstyle_color_convert_rgb_to_hsv (rgba, &hue, &saturation, &value);
+ for (gint x = 0; x < GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE; ++x)
+ {
+ saturation = x * HSV_TO_SCALE_FACTOR;
+ gstyle_color_convert_hsv_to_rgb (hue, saturation, value, &dst_rgba);
+ data [x] = pack_rgba24 (&dst_rgba);
+ }
+
+ gstyle_color_scale_set_custom_data (scale, data);
+ g_free (data);
+}
+
+static void
+update_hsv_value_color_ramp (GstyleColorPanel *self,
+ GstyleColorScale *scale,
+ GdkRGBA *rgba)
+{
+ gdouble hue, saturation, value;
+ guint32 *data;
+ GdkRGBA dst_rgba = {0};
+
+ /* TODO: malloc in init and keep data around */
+ data = g_malloc0 (GSTYLE_COLOR_SCALE_CUSTOM_DATA_BYTE_SIZE);
+ gstyle_color_convert_rgb_to_hsv (rgba, &hue, &saturation, &value);
+ for (gint x = 0; x < GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE; ++x)
+ {
+ value = x * HSV_TO_SCALE_FACTOR;
+ gstyle_color_convert_hsv_to_rgb (hue, saturation, value, &dst_rgba);
+ data [x] = pack_rgba24 (&dst_rgba);
+ }
+
+ gstyle_color_scale_set_custom_data (scale, data);
+ g_free (data);
+}
+
+static void
+update_rgb_red_color_ramp (GstyleColorPanel *self,
+ GstyleColorScale *scale,
+ GdkRGBA *rgba)
+{
+ guint32 *data;
+ GdkRGBA dst_rgba = *rgba;
+
+ /* TODO: malloc in init and keep data around */
+ data = g_malloc0 (GSTYLE_COLOR_SCALE_CUSTOM_DATA_BYTE_SIZE);
+ for (gint x = 0; x < GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE; ++x)
+ {
+ dst_rgba.red = x / 256.0;
+ data [x] = pack_rgba24 (&dst_rgba);
+ }
+
+ gstyle_color_scale_set_custom_data (scale, data);
+ g_free (data);
+}
+
+static void
+update_rgb_green_color_ramp (GstyleColorPanel *self,
+ GstyleColorScale *scale,
+ GdkRGBA *rgba)
+{
+ guint32 *data;
+ GdkRGBA dst_rgba = *rgba;
+
+ /* TODO: malloc in init and keep data around */
+ data = g_malloc0 (GSTYLE_COLOR_SCALE_CUSTOM_DATA_BYTE_SIZE);
+ for (gint x = 0; x < GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE; ++x)
+ {
+ dst_rgba.green = x / 256.0;
+ data [x] = pack_rgba24 (&dst_rgba);
+ }
+
+ gstyle_color_scale_set_custom_data (scale, data);
+ g_free (data);
+}
+
+static void
+update_rgb_blue_color_ramp (GstyleColorPanel *self,
+ GstyleColorScale *scale,
+ GdkRGBA *rgba)
+{
+ guint32 *data;
+ GdkRGBA dst_rgba = *rgba;
+
+ /* TODO: malloc in init and keep data around */
+ data = g_malloc0 (GSTYLE_COLOR_SCALE_CUSTOM_DATA_BYTE_SIZE);
+ for (gint x = 0; x < GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE; ++x)
+ {
+ dst_rgba.blue = x / 256.0;
+ data [x] = pack_rgba24 (&dst_rgba);
+ }
+
+ gstyle_color_scale_set_custom_data (scale, data);
+ g_free (data);
+}
+
+static void
+update_lab_l_color_ramp (GstyleColorPanel *self,
+ GstyleColorScale *scale,
+ GdkRGBA *rgba)
+{
+ GstyleCielab lab;
+ GdkRGBA dst_rgba = {0};
+ guint32 *data;
+
+ /* TODO: malloc in init and keep data around */
+ gstyle_color_convert_rgb_to_cielab (rgba, &lab);
+ data = g_malloc0 (GSTYLE_COLOR_SCALE_CUSTOM_DATA_BYTE_SIZE);
+ for (gint x = 0; x < GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE; ++x)
+ {
+ lab.l = x * CIELAB_L_TO_SCALE_FACTOR;
+ gstyle_color_convert_cielab_to_rgb (&lab, &dst_rgba);
+ data [x] = pack_rgba24 (&dst_rgba);
+ }
+
+ gstyle_color_scale_set_custom_data (scale, data);
+ g_free (data);
+}
+
+static void
+update_lab_a_color_ramp (GstyleColorPanel *self,
+ GstyleColorScale *scale,
+ GdkRGBA *rgba)
+{
+ GstyleCielab lab;
+ GdkRGBA dst_rgba = {0};
+ guint32 *data;
+
+ /* TODO: malloc in init and keep data around */
+ gstyle_color_convert_rgb_to_cielab (rgba, &lab);
+ data = g_malloc0 (GSTYLE_COLOR_SCALE_CUSTOM_DATA_BYTE_SIZE);
+ for (gint x = 0; x < GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE; ++x)
+ {
+ lab.a = x - 128;
+ gstyle_color_convert_cielab_to_rgb (&lab, &dst_rgba);
+ data [x] = pack_rgba24 (&dst_rgba);
+ }
+
+ gstyle_color_scale_set_custom_data (scale, data);
+ g_free (data);
+}
+
+static void
+update_lab_b_color_ramp (GstyleColorPanel *self,
+ GstyleColorScale *scale,
+ GdkRGBA *rgba)
+{
+ GstyleCielab lab;
+ GdkRGBA dst_rgba = {0};
+ guint32 *data;
+
+ /* TODO: malloc in init and keep data around */
+ gstyle_color_convert_rgb_to_cielab (rgba, &lab);
+ data = g_malloc0 (GSTYLE_COLOR_SCALE_CUSTOM_DATA_BYTE_SIZE);
+ for (gint x = 0; x < GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE; ++x)
+ {
+ lab.b = x - 128;
+ gstyle_color_convert_cielab_to_rgb (&lab, &dst_rgba);
+ data [x] = pack_rgba24 (&dst_rgba);
+ }
+
+ gstyle_color_scale_set_custom_data (scale, data);
+ g_free (data);
+}
+
+static void
+update_ref_color_ramp (GstyleColorPanel *self,
+ GdkRGBA *rgba)
+{
+ switch (self->current_comp)
+ {
+ case GSTYLE_COLOR_COMPONENT_HSV_H:
+ update_hsv_hue_color_ramp (self, self->ref_scale, rgba);
+ break;
+
+ case GSTYLE_COLOR_COMPONENT_HSV_S:
+ update_hsv_saturation_color_ramp (self, self->ref_scale, rgba);
+ break;
+
+ case GSTYLE_COLOR_COMPONENT_HSV_V:
+ update_hsv_value_color_ramp (self, self->ref_scale, rgba);
+ break;
+
+ case GSTYLE_COLOR_COMPONENT_LAB_L:
+ update_lab_l_color_ramp (self, self->ref_scale, rgba);
+ break;
+
+ case GSTYLE_COLOR_COMPONENT_LAB_A:
+ update_lab_a_color_ramp (self, self->ref_scale, rgba);
+ break;
+
+ case GSTYLE_COLOR_COMPONENT_LAB_B:
+ update_lab_b_color_ramp (self, self->ref_scale, rgba);
+ break;
+
+ case GSTYLE_COLOR_COMPONENT_RGB_RED:
+ update_rgb_red_color_ramp (self, self->ref_scale, rgba);
+ break;
+
+ case GSTYLE_COLOR_COMPONENT_RGB_GREEN:
+ update_rgb_green_color_ramp (self, self->ref_scale, rgba);
+ break;
+
+ case GSTYLE_COLOR_COMPONENT_RGB_BLUE:
+ update_rgb_blue_color_ramp (self, self->ref_scale, rgba);
+ break;
+
+ case N_GSTYLE_COLOR_COMPONENT:
+ case GSTYLE_COLOR_COMPONENT_NONE:
+ default:
+ break;
+ }
+}
+
+static void
+update_color_strings (GstyleColorPanel *self,
+ GstyleColor *color)
+{
+ gchar *str;
+ g_autofree gchar *str_rgb = NULL;
+ g_autofree gchar *str_rgba = NULL;
+ const gchar *label;
+
+ str = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_RGB_HEX3);
+ label = gtk_label_get_label (self->res_hex3_label);
+ if (g_strcmp0 (str, label) != 0)
+ gtk_label_set_label (self->res_hex3_label, str);
+ g_free (str);
+
+ str = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_RGB_HEX6);
+ label = gtk_label_get_label (self->res_hex6_label);
+ if (g_strcmp0 (str, label) != 0)
+ gtk_label_set_label (self->res_hex6_label, str);
+ g_free (str);
+
+ if (self->preferred_unit == GSTYLE_COLOR_UNIT_PERCENT)
+ {
+ str_rgb = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_RGB_PERCENT);
+ str_rgba = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_RGBA_PERCENT);
+ }
+ else if (self->preferred_unit == GSTYLE_COLOR_UNIT_VALUE)
+ {
+ str_rgb = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_RGB);
+ str_rgba = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_RGBA);
+ }
+ else
+ g_assert_not_reached ();
+
+ label = gtk_label_get_label (self->res_rgb_label);
+ if (g_strcmp0 (str_rgb, label) != 0)
+ gtk_label_set_label (self->res_rgb_label, str_rgb);
+
+ label = gtk_label_get_label (self->res_rgba_label);
+ if (g_strcmp0 (str_rgba, label) != 0)
+ gtk_label_set_label (self->res_rgba_label, str_rgba);
+
+ str = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_HSL);
+ label = gtk_label_get_label (self->res_hsl_label);
+ if (g_strcmp0 (str, label) != 0)
+ gtk_label_set_label (self->res_hsl_label, str);
+ g_free (str);
+
+ str = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_HSLA);
+ label = gtk_label_get_label (self->res_hsla_label);
+ if (g_strcmp0 (str, label) != 0)
+ gtk_label_set_label (self->res_hsla_label, str);
+ g_free (str);
+}
+
+static void
+color_picked_cb (GstyleColorPanel *self,
+ GdkRGBA *rgba)
+{
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ gstyle_color_plane_set_rgba (self->color_plane, rgba);
+}
+
+static void
+grab_released_cb (GstyleColorPanel *self)
+{
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ /* FIX: release the eyedropper here cause a Segfault */
+ //g_clear_object (&self->eyedropper);
+}
+
+static void
+picker_button_clicked_cb (GstyleColorPanel *self,
+ GtkButton *picker_button)
+{
+ GdkEvent *event;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+ g_assert (GTK_IS_BUTTON (picker_button));
+
+ event = gtk_get_current_event ();
+ g_assert (event != NULL);
+
+ self->eyedropper = g_object_new (GSTYLE_TYPE_EYEDROPPER,
+ "source-event", event,
+ NULL);
+ gdk_event_free (event);
+
+ g_signal_connect_swapped (self->eyedropper,
+ "color-picked",
+ G_CALLBACK (color_picked_cb),
+ self);
+
+ g_signal_connect_swapped (self->eyedropper,
+ "grab-released",
+ G_CALLBACK (grab_released_cb),
+ self);
+}
+
+static void
+palette_row_activated_cb (GstyleColorPanel *self,
+ GstylePalette *palette,
+ gint position,
+ GstylePaletteWidget *palette_widget)
+{
+ const GstyleColor *color;
+ GdkRGBA rgba = {0};
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+ g_assert (GSTYLE_IS_PALETTE (palette));
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (palette_widget));
+
+ color = gstyle_palette_get_color_at_index (palette, position);
+ gstyle_color_fill_rgba ((GstyleColor *)color, &rgba);
+
+ gstyle_color_panel_set_rgba (self, &rgba);
+}
+
+static void
+update_sub_panels (GstyleColorPanel *self,
+ GdkRGBA rgba)
+{
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ rgba.alpha = gtk_adjustment_get_value (self->adj_alpha) / 100.0;
+ gstyle_color_set_rgba (self->old_color, &rgba);
+ update_color_strings (self, self->old_color);
+
+ rgba.alpha = 1.0;
+ /* TODO: compare with old value and update only when needed */
+
+ update_hsv_hue_color_ramp (self, self->components [GSTYLE_COLOR_COMPONENT_HSV_H].scale, &rgba);
+ update_hsv_saturation_color_ramp (self, self->components [GSTYLE_COLOR_COMPONENT_HSV_S].scale, &rgba);
+ update_hsv_value_color_ramp (self, self->components [GSTYLE_COLOR_COMPONENT_HSV_V].scale, &rgba);
+
+ update_rgb_red_color_ramp (self, self->components [GSTYLE_COLOR_COMPONENT_RGB_RED].scale, &rgba);
+ update_rgb_green_color_ramp (self, self->components [GSTYLE_COLOR_COMPONENT_RGB_GREEN].scale, &rgba);
+ update_rgb_blue_color_ramp (self, self->components [GSTYLE_COLOR_COMPONENT_RGB_BLUE].scale, &rgba);
+
+ update_lab_l_color_ramp (self, self->components [GSTYLE_COLOR_COMPONENT_LAB_L].scale, &rgba);
+ update_lab_a_color_ramp (self, self->components [GSTYLE_COLOR_COMPONENT_LAB_A].scale, &rgba);
+ update_lab_b_color_ramp (self, self->components [GSTYLE_COLOR_COMPONENT_LAB_B].scale, &rgba);
+
+ update_ref_color_ramp (self, &rgba);
+}
+
+static void
+component_toggled_cb (GstyleColorPanel *self,
+ GtkToggleButton *toggle)
+{
+ GdkRGBA rgba = {0};
+ GtkAdjustment *adj;
+
+ if (!gtk_toggle_button_get_active (toggle))
+ {
+ gtk_toggle_button_set_active (toggle, TRUE);
+ return;
+ }
+
+ for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
+ {
+ if (toggle != self->components [i].toggle)
+ {
+ g_signal_handler_block (self->components [i].toggle, self->components [i].toggle_handler_id);
+ gtk_toggle_button_set_active (self->components [i].toggle, FALSE);
+ g_signal_handler_unblock (self->components [i].toggle, self->components [i].toggle_handler_id);
+ }
+ else
+ {
+ self->current_comp = i;
+ gstyle_color_plane_set_mode (self->color_plane, component_to_plane_mode [i]);
+ adj = gstyle_color_plane_get_component_adjustment (self->color_plane, i);
+ gtk_range_set_adjustment (GTK_RANGE (self->ref_scale), adj);
+
+ gstyle_color_plane_get_rgba (self->color_plane, &rgba);
+ update_ref_color_ramp (self, &rgba);
+ }
+ }
+}
+
+static void
+search_list_add_color (GstyleColorPanel *self,
+ GstyleColor *color)
+{
+ GtkWidget *color_widget;
+
+ color_widget = g_object_new (GSTYLE_TYPE_COLOR_WIDGET,
+ "color", color,
+ "visible", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ NULL);
+
+ gtk_list_box_insert (GTK_LIST_BOX (self->search_strings_list), color_widget, -1);
+}
+
+static void
+search_color_entry_changed_cb (GstyleColorPanel *self,
+ GtkSearchEntry *entry)
+{
+ g_autoptr (GPtrArray) ar = NULL;
+ GstyleColor *color;
+ const gchar *str;
+ GList *children;
+ GList *l;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+ g_assert (GTK_IS_SEARCH_ENTRY (entry));
+
+ str = gtk_entry_get_text(GTK_ENTRY(entry));
+ if (gstyle_str_empty0 (str))
+ {
+ gtk_widget_set_visible (self->search_strings_popover, FALSE);
+ return;
+ }
+
+ children = gtk_container_get_children (GTK_CONTAINER (self->search_strings_list));
+ for (l = children; l != NULL; l = g_list_next (l))
+ gtk_widget_destroy (GTK_WIDGET (l->data));
+
+ if (str[0] == '#' || g_str_has_prefix (str, "rgb") || g_str_has_prefix (str, "hsl"))
+ {
+ color = gstyle_color_new_from_string (NULL, str);
+ if (color != NULL)
+ {
+ search_list_add_color (self, color);
+ gtk_widget_set_visible (self->search_strings_popover, TRUE);
+ }
+ }
+ else
+ {
+ ar = gstyle_color_fuzzy_parse_color_string (str);
+ for (gint i = 0; i < ar->len; ++i)
+ {
+ color = g_ptr_array_index (ar, i);
+ search_list_add_color (self, color);
+ }
+
+ gtk_widget_set_visible (self->search_strings_popover, (ar->len > 0));
+ }
+}
+
+static void
+adj_alpha_value_changed_cb (GstyleColorPanel *self,
+ GtkAdjustment *adj)
+{
+ gdouble alpha;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ alpha = gtk_adjustment_get_value (self->adj_alpha) / 100.0;
+ gstyle_color_set_alpha (self->old_color, alpha);
+ update_color_strings (self, self->old_color);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RGBA]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_XYZ]);
+}
+
+static GIcon *
+get_degree_icon (GstyleColorPanel *self)
+{
+ GIcon *icon;
+ g_autoptr (GFile) file = NULL;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ file = g_file_new_for_uri ("resource:///org/gnome/libgstyle/icons/unit-degree-symbolic.svg");
+ icon = g_file_icon_new (file);
+
+ return icon;
+}
+
+static GIcon *
+get_percent_icon (GstyleColorPanel *self)
+{
+ GIcon *icon;
+ g_autoptr (GFile) file = NULL;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ file = g_file_new_for_uri ("resource:///org/gnome/libgstyle/icons/unit-percent-symbolic.svg");
+ icon = g_file_icon_new (file);
+
+ return icon;
+}
+
+static void
+set_preferred_unit (GstyleColorPanel *self,
+ GstyleColorUnit preferred_unit)
+{
+ GIcon *icon;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ if (self->preferred_unit != preferred_unit)
+ {
+ self->preferred_unit = preferred_unit;
+ if (preferred_unit == GSTYLE_COLOR_UNIT_PERCENT)
+ icon = self->percent_icon;
+ else if (preferred_unit == GSTYLE_COLOR_UNIT_VALUE)
+ icon = NULL;
+ else
+ g_assert_not_reached ();
+
+ gstyle_color_plane_set_preferred_unit (self->color_plane, preferred_unit);
+ //update_color_strings (self, self->old_color);
+
+ gtk_entry_set_icon_from_gicon (GTK_ENTRY (self->components [GSTYLE_COLOR_COMPONENT_RGB_RED].spin),
+ GTK_ENTRY_ICON_SECONDARY, icon);
+ gtk_entry_set_icon_from_gicon (GTK_ENTRY (self->components [GSTYLE_COLOR_COMPONENT_RGB_GREEN].spin),
+ GTK_ENTRY_ICON_SECONDARY, icon);
+ gtk_entry_set_icon_from_gicon (GTK_ENTRY (self->components [GSTYLE_COLOR_COMPONENT_RGB_BLUE].spin),
+ GTK_ENTRY_ICON_SECONDARY, icon);
+ }
+}
+
+/* TODO: only keep one transformation func ? */
+static gboolean
+rgba_plane_to_new_color (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ GstyleColorPanel *self = (GstyleColorPanel *)user_data;
+ GdkRGBA *rgba = {0};
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ rgba = g_value_get_boxed (from_value);
+ rgba->alpha = 1.0;
+ g_value_set_boxed (to_value, rgba);
+
+ update_sub_panels (self, *rgba);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RGBA]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_XYZ]);
+
+ return TRUE;
+}
+
+static gboolean
+rgba_new_color_to_plane (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ GstyleColorPanel *self = (GstyleColorPanel *)user_data;
+ GdkRGBA *rgba = {0};
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ rgba = g_value_get_boxed (from_value);
+ rgba->alpha = 1.0;
+ g_value_set_boxed (to_value, rgba);
+
+ update_sub_panels (self, *rgba);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RGBA]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_XYZ]);
+
+ return TRUE;
+}
+
+static void
+prefs_button_notify_active_cb (GstyleColorPanel *self,
+ GParamSpec *pspec,
+ GtkToggleButton *button)
+{
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+ g_assert (GTK_IS_TOGGLE_BUTTON (button));
+
+ if (gtk_toggle_button_get_active (button))
+ self->last_checked_prefs_button = button;
+ else
+ self->last_checked_prefs_button = NULL;
+}
+
+static void
+slide_is_closing_cb (GstyleColorPanel *self,
+ GstyleSlidein *slidein)
+{
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+ g_assert (GSTYLE_IS_SLIDEIN (slidein));
+
+ if (self->last_checked_prefs_button != NULL)
+ gtk_toggle_button_set_active (self->last_checked_prefs_button, FALSE);
+}
+
+static void
+update_palette_name (GstyleColorPanel *self,
+ GstylePalette *palette)
+{
+ const gchar *name;
+ g_autofree gchar *full_name = NULL;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+ g_assert (palette == NULL || GSTYLE_IS_PALETTE (palette));
+
+ if (palette != NULL &&
+ (name = gstyle_palette_get_name (palette)) &&
+ !gstyle_str_empty0 (name))
+ full_name = g_strconcat (_("Palette: "), name, NULL);
+ else
+ full_name = g_strdup (_("Palette"));
+
+ gtk_button_set_label (GTK_BUTTON (self->palette_toggle), full_name);
+}
+
+static void
+palette_selected_notify_cb (GstyleColorPanel *self,
+ GParamSpec *pspec,
+ GstylePaletteWidget *palette_widget)
+{
+ GstylePalette *palette;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (palette_widget));
+
+ palette = gstyle_palette_widget_get_selected_palette (palette_widget);
+ update_palette_name (self, palette);
+}
+
+void
+_gstyle_color_panel_update_prefs_page (GstyleColorPanel *self,
+ const gchar *page_name)
+{
+ GstyleColorPanelPrefs prefs_type;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ if (g_strcmp0 (page_name, PREFS_COMPONENTS_PAGE) == 0)
+ prefs_type = GSTYLE_COLOR_PANEL_PREFS_COMPONENTS;
+ else if (g_strcmp0 (page_name, PREFS_COLOR_STRINGS_PAGE) == 0)
+ prefs_type = GSTYLE_COLOR_PANEL_PREFS_COLOR_STRINGS;
+ else if (g_strcmp0 (page_name, PREFS_PALETTES_PAGE) == 0)
+ prefs_type = GSTYLE_COLOR_PANEL_PREFS_PALETTES;
+ else if (g_strcmp0 (page_name, PREFS_PALETTES_LIST_PAGE) == 0)
+ prefs_type = GSTYLE_COLOR_PANEL_PREFS_PALETTES_LIST;
+ else
+ g_assert_not_reached ();
+
+ g_signal_emit (self, signals [UPDATE_PREFS], 0, prefs_type);
+}
+
+static void
+replace_prefs_page (GstyleColorPanel *self,
+ GtkWidget *new_page,
+ GtkWidget **bin,
+ const gchar *page_name)
+{
+ if (*bin != NULL)
+ {
+ gtk_container_remove (GTK_CONTAINER (self->prefs_stack), *bin);
+ *bin = NULL;
+ }
+
+ if (new_page != NULL)
+ {
+ *bin = new_page;
+ gtk_stack_add_named (self->prefs_stack, new_page, page_name);
+ }
+}
+
+void
+gstyle_color_panel_set_prefs_pages (GstyleColorPanel *self,
+ GtkWidget *components_page,
+ GtkWidget *colorstrings_page,
+ GtkWidget *palettes_page,
+ GtkWidget *palettes_list_page)
+{
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+ g_assert (components_page == NULL || GTK_IS_WIDGET (components_page));
+ g_assert (colorstrings_page == NULL || GTK_IS_WIDGET (colorstrings_page));
+ g_assert (palettes_page == NULL || GTK_IS_WIDGET (palettes_page));
+ g_assert (palettes_list_page == NULL || GTK_IS_WIDGET (palettes_list_page));
+
+ replace_prefs_page (self, components_page, &self->components_prefs_bin, PREFS_COMPONENTS_PAGE);
+ replace_prefs_page (self, colorstrings_page, &self->color_strings_prefs_bin, PREFS_COLOR_STRINGS_PAGE);
+ replace_prefs_page (self, palettes_page, &self->palettes_prefs_bin, PREFS_PALETTES_PAGE);
+ replace_prefs_page (self, palettes_list_page, &self->palettes_list_prefs_bin, PREFS_PALETTES_LIST_PAGE);
+}
+
+static void
+bar_toggled_cb (GtkToggleButton *toggle,
+ GstyleRevealer *reveal)
+{
+ GtkStyleContext *context;
+ gboolean active;
+
+ g_assert (GTK_IS_TOGGLE_BUTTON (toggle));
+ g_assert (GSTYLE_IS_REVEALER (reveal));
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (toggle));
+ active = gtk_toggle_button_get_active (toggle);
+ gstyle_revealer_set_reveal_child (reveal, active);
+
+ if (active)
+ gtk_style_context_remove_class (context, "dim-label");
+ else
+ gtk_style_context_add_class (context, "dim-label");
+}
+
+static void
+setup_ui (GstyleColorPanel *self)
+{
+ GdkRGBA rgba = {0.26, 0.5, 0.5};
+ GtkAdjustment *adj;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
+ {
+ g_autofree gchar *toggle = g_strconcat (comp_names [i], "_toggle", NULL);
+ g_autofree gchar *spin = g_strconcat (comp_names [i], "_spin", NULL);
+ g_autofree gchar *scale = g_strconcat (comp_names [i], "_scale", NULL);
+
+ self->components [i].toggle =
+ GTK_TOGGLE_BUTTON (gtk_widget_get_template_child (GTK_WIDGET (self),
+ GSTYLE_TYPE_COLOR_PANEL,
+ toggle));
+ self->components [i].spin =
+ GTK_SPIN_BUTTON (gtk_widget_get_template_child (GTK_WIDGET (self),
+ GSTYLE_TYPE_COLOR_PANEL,
+ spin));
+ self->components [i].scale =
+ GSTYLE_COLOR_SCALE (gtk_widget_get_template_child (GTK_WIDGET (self),
+ GSTYLE_TYPE_COLOR_PANEL,
+ scale));
+
+ adj = gstyle_color_plane_get_component_adjustment (self->color_plane, i);
+ gtk_range_set_adjustment (GTK_RANGE (self->components [i].scale), adj);
+ gtk_spin_button_set_adjustment (self->components [i].spin, adj);
+
+ self->components [i].toggle_handler_id =
+ g_signal_connect_swapped (self->components [i].toggle,
+ "toggled",
+ G_CALLBACK (component_toggled_cb),
+ self);
+ }
+
+ self->current_comp = GSTYLE_COLOR_COMPONENT_HSV_H;
+ gtk_toggle_button_set_active (self->components [GSTYLE_COLOR_COMPONENT_HSV_H].toggle, TRUE);
+
+ self->adj_alpha = gtk_adjustment_new (50.0, 0.0, 100.0, 0.1, 1.0, 0.0);
+ gtk_range_set_adjustment (GTK_RANGE (self->alpha_scale), self->adj_alpha);
+ g_signal_connect_swapped (self->adj_alpha, "value-changed",
+ G_CALLBACK (adj_alpha_value_changed_cb), self);
+
+ self->new_color = gstyle_color_new (NULL, GSTYLE_COLOR_KIND_RGB_HEX6, 0.0, 0.0, 0.0, 100);
+ gstyle_color_widget_set_color (self->new_swatch, self->new_color);
+ g_object_set (self->new_swatch,
+ "dnd-lock", GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_ALPHA |
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_KIND |
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_NAME,
+ NULL);
+
+ self->old_color = gstyle_color_new (NULL, GSTYLE_COLOR_KIND_RGB_HEX6, 0.0, 0.0, 0.0, 50);
+ gstyle_color_widget_set_color (self->old_swatch, self->old_color);
+ g_object_set (self->old_swatch,
+ "dnd-lock", GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_ALL,
+ NULL);
+
+ bar_toggled_cb (self->components_toggle, self->scale_reveal);
+ g_signal_connect (self->components_toggle, "toggled",
+ G_CALLBACK (bar_toggled_cb), self->scale_reveal);
+
+ bar_toggled_cb (self->strings_toggle, self->string_reveal);
+ g_signal_connect (self->strings_toggle, "toggled",
+ G_CALLBACK (bar_toggled_cb), self->string_reveal);
+
+ bar_toggled_cb (self->palette_toggle, self->palette_reveal);
+ g_signal_connect (self->palette_toggle, "toggled",
+ G_CALLBACK (bar_toggled_cb), self->palette_reveal);
+
+ /* default value to start with */
+ g_object_bind_property_full (self->color_plane, "rgba", self->new_color,
+ "rgba", G_BINDING_BIDIRECTIONAL,
+ rgba_plane_to_new_color, rgba_new_color_to_plane,
+ self, NULL);
+ gstyle_color_plane_set_rgba (self->color_plane, &rgba);
+
+ gtk_popover_set_relative_to (GTK_POPOVER (self->search_strings_popover),
+ GTK_WIDGET (self->search_color_entry));
+ g_signal_connect_swapped (self->search_color_entry, "search-changed",
+ G_CALLBACK (search_color_entry_changed_cb),
+ self);
+
+ gtk_entry_set_icon_from_gicon (GTK_ENTRY (self->components [GSTYLE_COLOR_COMPONENT_HSV_H].spin),
+ GTK_ENTRY_ICON_SECONDARY, self->degree_icon);
+ gtk_entry_set_icon_from_gicon (GTK_ENTRY (self->components [GSTYLE_COLOR_COMPONENT_HSV_S].spin),
+ GTK_ENTRY_ICON_SECONDARY, self->percent_icon);
+ gtk_entry_set_icon_from_gicon (GTK_ENTRY (self->components [GSTYLE_COLOR_COMPONENT_HSV_V].spin),
+ GTK_ENTRY_ICON_SECONDARY, self->percent_icon);
+ gtk_entry_set_icon_from_gicon (GTK_ENTRY (self->components [GSTYLE_COLOR_COMPONENT_LAB_L].spin),
+ GTK_ENTRY_ICON_SECONDARY, self->percent_icon);
+ set_preferred_unit (self, GSTYLE_COLOR_UNIT_VALUE);
+
+ g_signal_connect_swapped (self->palette_widget, "activated",
+ G_CALLBACK (palette_row_activated_cb),
+ self);
+ g_signal_connect_swapped (self->palette_widget, "notify::selected-palette-id",
+ G_CALLBACK (palette_selected_notify_cb),
+ self);
+
+ g_signal_connect_swapped (self->picker_button, "clicked",
+ G_CALLBACK (picker_button_clicked_cb),
+ self);
+
+ g_signal_connect_swapped (self->prefs_slidein, "closing",
+ G_CALLBACK (slide_is_closing_cb),
+ self);
+
+ g_signal_connect_swapped (self->components_prefs_button,
+ "notify::active",
+ G_CALLBACK (prefs_button_notify_active_cb),
+ self);
+ g_signal_connect_swapped (self->color_strings_prefs_button,
+ "notify::active",
+ G_CALLBACK (prefs_button_notify_active_cb),
+ self);
+ g_signal_connect_swapped (self->palettes_prefs_button,
+ "notify::active",
+ G_CALLBACK (prefs_button_notify_active_cb),
+ self);
+ g_signal_connect_swapped (self->palettes_list_prefs_button,
+ "notify::active",
+ G_CALLBACK (prefs_button_notify_active_cb),
+ self);
+}
+
+/**
+ * gstyle_color_panel_show_palette:
+ * @self: A #GstyleColorPanel.
+ * @palette: A GstylePalette.
+ *
+ * Show the @palette and update it's name in the bar.
+ *
+ */
+void
+gstyle_color_panel_show_palette (GstyleColorPanel *self,
+ GstylePalette *palette)
+{
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+ g_assert (GSTYLE_IS_PALETTE (palette));
+
+ if(gstyle_palette_widget_show_palette (self->palette_widget, palette))
+ update_palette_name (self, palette);
+}
+
+static void
+gstyle_color_panel_set_strings_visible (GstyleColorPanel *self,
+ GstyleColorPanelStringsVisibleFlags flags)
+{
+ gboolean hex3_visible;
+ gboolean hex6_visible;
+ gboolean rgb_visible;
+ gboolean rgba_visible;
+ gboolean hsl_visible;
+ gboolean hsla_visible;
+
+ g_assert (GSTYLE_IS_COLOR_PANEL (self));
+
+ if (self->strings_visible_flags != flags)
+ {
+ self->strings_visible_flags = flags;
+
+ hex3_visible = !!(flags & GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HEX3);
+ hex6_visible = !!(flags & GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HEX6);
+ rgb_visible = !!(flags & GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_RGB);
+ rgba_visible = !!(flags & GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_RGBA);
+ hsl_visible = !!(flags & GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HSL);
+ hsla_visible = !!(flags & GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HSLA);
+
+ gtk_widget_set_visible (GTK_WIDGET (self->hex3_label), hex3_visible);
+ gtk_widget_set_visible (GTK_WIDGET (self->res_hex3_label), hex3_visible);
+
+ gtk_widget_set_visible (GTK_WIDGET (self->hex6_label), hex6_visible);
+ gtk_widget_set_visible (GTK_WIDGET (self->res_hex6_label), hex6_visible);
+
+ gtk_widget_set_visible (GTK_WIDGET (self->rgb_label), rgb_visible);
+ gtk_widget_set_visible (GTK_WIDGET (self->res_rgb_label), rgb_visible);
+
+ gtk_widget_set_visible (GTK_WIDGET (self->rgba_label), rgba_visible);
+ gtk_widget_set_visible (GTK_WIDGET (self->res_rgba_label), rgba_visible);
+
+ gtk_widget_set_visible (GTK_WIDGET (self->hsl_label), hsl_visible);
+ gtk_widget_set_visible (GTK_WIDGET (self->res_hsl_label), hsl_visible);
+
+ gtk_widget_set_visible (GTK_WIDGET (self->hsla_label), hsla_visible);
+ gtk_widget_set_visible (GTK_WIDGET (self->res_hsla_label), hsla_visible);
+ }
+}
+
+GstyleColorPanel *
+gstyle_color_panel_new (void)
+{
+ return g_object_new (GSTYLE_TYPE_COLOR_PANEL, NULL);
+}
+
+static void
+gstyle_color_panel_dispose (GObject *object)
+{
+ GstyleColorPanel *self = (GstyleColorPanel *)object;
+
+ g_clear_object (&self->new_color);
+ g_clear_object (&self->default_provider);
+ g_clear_object (&self->degree_icon);
+ g_clear_object (&self->percent_icon);
+ gstyle_color_panel_set_prefs_pages (self, NULL, NULL, NULL, NULL);
+
+ G_OBJECT_CLASS (gstyle_color_panel_parent_class)->dispose (object);
+}
+
+static void
+gstyle_color_panel_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleColorPanel *self = GSTYLE_COLOR_PANEL (object);
+ GdkRGBA rgba = {0};
+ GstyleXYZ xyz;
+
+ switch (prop_id)
+ {
+ case PROP_HSV_VISIBLE:
+ g_value_set_boolean (value, gtk_widget_get_visible (self->hsv_grid));
+ break;
+ case PROP_LAB_VISIBLE:
+ g_value_set_boolean (value, gtk_widget_get_visible (self->lab_grid));
+ break;
+
+ case PROP_RGB_VISIBLE:
+ g_value_set_boolean (value, gtk_widget_get_visible (self->rgb_grid));
+ break;
+
+ case PROP_STRINGS_VISIBLE:
+ g_value_set_flags (value, self->strings_visible_flags);
+ break;
+
+ case PROP_RGBA:
+ gstyle_color_panel_get_rgba (self, &rgba);
+ g_value_set_boxed (value, &rgba);
+ break;
+
+ case PROP_RGB_UNIT:
+ g_value_set_enum (value, self->preferred_unit);
+ break;
+
+ case PROP_XYZ:
+ gstyle_color_panel_get_xyz (self, &xyz);
+ g_value_set_boxed (value, &xyz);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_color_panel_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleColorPanel *self = GSTYLE_COLOR_PANEL (object);
+ GdkRGBA *rgba_p;
+ GstyleXYZ *xyz_p;
+
+ switch (prop_id)
+ {
+ case PROP_HSV_VISIBLE:
+ gtk_widget_set_visible (self->hsv_grid, g_value_get_boolean (value));
+ break;
+
+ case PROP_LAB_VISIBLE:
+ gtk_widget_set_visible (self->lab_grid, g_value_get_boolean (value));
+ break;
+
+ case PROP_RGB_VISIBLE:
+ gtk_widget_set_visible (self->rgb_grid, g_value_get_boolean (value));
+ break;
+
+ case PROP_STRINGS_VISIBLE:
+ gstyle_color_panel_set_strings_visible (self, g_value_get_flags (value));
+ break;
+
+ case PROP_RGB_UNIT:
+ set_preferred_unit (self, g_value_get_enum (value));
+ break;
+
+ case PROP_RGBA:
+ rgba_p = (GdkRGBA *)g_value_get_boxed (value);
+ if (rgba_p != NULL)
+ gstyle_color_panel_set_rgba (self, rgba_p);
+ break;
+
+ case PROP_XYZ:
+ xyz_p = (GstyleXYZ *)g_value_get_boxed (value);
+ if (xyz_p != NULL)
+ gstyle_color_panel_set_xyz (self, xyz_p);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_color_panel_class_init (GstyleColorPanelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gstyle_color_panel_dispose;
+ object_class->get_property = gstyle_color_panel_get_property;
+ object_class->set_property = gstyle_color_panel_set_property;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/libgstyle/ui/gstyle-color-panel.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, new_swatch);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, old_swatch);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, color_plane);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, hsv_grid);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, lab_grid);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, rgb_grid);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, components_controls);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, strings_controls);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, palette_controls);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, components_toggle);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, strings_toggle);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, palette_toggle);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, scale_reveal);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, string_reveal);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, palette_reveal);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, ref_scale);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, alpha_scale);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, palette_widget);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, res_hex3_label);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, res_hex6_label);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, res_rgb_label);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, res_rgba_label);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, res_hsl_label);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, res_hsla_label);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, hex3_label);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, hex6_label);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, rgb_label);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, rgba_label);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, hsl_label);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, hsla_label);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, prefs_stack);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, components_prefs_button);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, color_strings_prefs_button);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, palettes_prefs_button);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, palettes_list_prefs_button);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, search_color_entry);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, search_strings_popover);
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, search_strings_list);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, picker_button);
+
+ gtk_widget_class_bind_template_child (widget_class, GstyleColorPanel, prefs_slidein);
+
+ g_type_ensure (GSTYLE_TYPE_SLIDEIN);
+ g_type_ensure (GSTYLE_TYPE_COLOR_SCALE);
+ g_type_ensure (GSTYLE_TYPE_COLOR_PLANE);
+ g_type_ensure (GSTYLE_TYPE_COLOR_WIDGET);
+ g_type_ensure (GSTYLE_TYPE_REVEALER);
+ g_type_ensure (GSTYLE_TYPE_PALETTE_WIDGET);
+
+ for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
+ {
+ g_autofree gchar *toggle = g_strconcat (comp_names [i], "_toggle", NULL);
+ g_autofree gchar *spin = g_strconcat (comp_names [i], "_spin", NULL);
+ g_autofree gchar *scale = g_strconcat (comp_names [i], "_scale", NULL);
+
+ gtk_widget_class_bind_template_child_full (widget_class, toggle, FALSE, 0);
+ gtk_widget_class_bind_template_child_full (widget_class, spin, FALSE, 0);
+ gtk_widget_class_bind_template_child_full (widget_class, scale, FALSE, 0);
+ }
+
+ gtk_widget_class_set_css_name (widget_class, "gstylecolorpanel");
+
+ properties [PROP_RGBA] =
+ g_param_spec_boxed ("rgba",
+ "rgba",
+ "current color of the color plane",
+ GDK_TYPE_RGBA,
+ (G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_XYZ] =
+ g_param_spec_boxed ("xyz",
+ "xyz",
+ "current xyz color of the color plane",
+ GSTYLE_TYPE_XYZ,
+ (G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_HSV_VISIBLE] =
+ g_param_spec_boolean ("hsv-visible",
+ "hsv-visible",
+ "Visibility of the HSV components",
+ TRUE,
+ (G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_LAB_VISIBLE] =
+ g_param_spec_boolean ("lab-visible",
+ "lab-visible",
+ "Visibility of the LAB components",
+ TRUE,
+ (G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_RGB_VISIBLE] =
+ g_param_spec_boolean ("rgb-visible",
+ "rgb-visible",
+ "Visibility of the RGB components",
+ TRUE,
+ (G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_RGB_UNIT] =
+ g_param_spec_enum ("rgb-unit",
+ "rgb-unit",
+ "Units used by the RGB components and strings",
+ GSTYLE_TYPE_COLOR_UNIT,
+ GSTYLE_COLOR_UNIT_PERCENT,
+ (G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_STRINGS_VISIBLE] =
+ g_param_spec_flags ("strings-visible",
+ "strings-visible",
+ "Color strings visible",
+ GSTYLE_TYPE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS,
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HEX3 |
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HEX6 |
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_RGB |
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_RGBA |
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HSL |
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HSLA ,
+ (G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ signals [UPDATE_PREFS] = g_signal_new ("update-prefs",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ GSTYLE_TYPE_COLOR_PANEL_PREFS);
+
+ /* signals [DISCONNECT_PREFS] = g_signal_new ("disconnect-prefs", */
+ /* G_TYPE_FROM_CLASS (klass), */
+ /* G_SIGNAL_RUN_LAST, */
+ /* 0, */
+ /* NULL, NULL, NULL, */
+ /* G_TYPE_NONE, */
+ /* 2, */
+ /* GSTYLE_TYPE_COLOR_PANEL_PREFS, */
+ /* GTK_TYPE_WIDGET); */
+}
+
+static void
+gstyle_color_panel_init (GstyleColorPanel *self)
+{
+ GtkStyleContext *context;
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+ gstyle_color_panel_actions_init (self);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ self->default_provider = gstyle_css_provider_init_default (gtk_style_context_get_screen (context));
+ gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (),
+ "/org/gnome/libgstyle/icons");
+
+ self->degree_icon = get_degree_icon (self);
+ self->percent_icon = get_percent_icon (self);
+
+ self->preferred_unit = GSTYLE_COLOR_UNIT_VALUE;
+ setup_ui (self);
+}
+
+GType
+gstyle_color_panel_prefs_get_type (void)
+{
+ static GType type_id;
+ static const GEnumValue values[] = {
+ { GSTYLE_COLOR_PANEL_PREFS_COMPONENTS, "GSTYLE_COLOR_PANEL_PREFS_COMPONENTS", "components" },
+ { GSTYLE_COLOR_PANEL_PREFS_COLOR_STRINGS, "GSTYLE_COLOR_PANEL_PREFS_COLOR_STRINGS", "colorstrings" },
+ { GSTYLE_COLOR_PANEL_PREFS_PALETTES, "GSTYLE_COLOR_PANEL_PREFS_PALETTES", "palettes" },
+ { GSTYLE_COLOR_PANEL_PREFS_PALETTES_LIST, "GSTYLE_COLOR_PANEL_PREFS_PALETTES_LIST", "paletteslist" },
+ { 0 }
+ };
+
+ if (g_once_init_enter (&type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_enum_register_static ("GstyleColorPanelPrefs", values);
+ g_once_init_leave (&type_id, _type_id);
+ }
+
+ return type_id;
+}
+
+GType
+gstyle_color_panel_strings_visible_flags_get_type (void)
+{
+ static GType type_id;
+ static const GFlagsValue values[] = {
+ { GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_NONE, "GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_NONE", "none"
},
+ { GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HEX3, "GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HEX3", "hex3"
},
+ { GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HEX6, "GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HEX6", "hex6"
},
+ { GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_RGB, "GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_RGB", "rgb"
},
+ { GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_RGBA, "GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_RGBA", "rgba"
},
+ { GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HSL, "GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HSL", "hsl"
},
+ { GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HSLA, "GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HSLA", "hsla"
},
+ { 0 }
+ };
+
+ if (g_once_init_enter (&type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_flags_register_static ("GstyleColorPanelStringsVisibleFlags", values);
+ g_once_init_leave (&type_id, _type_id);
+ }
+
+ return type_id;
+}
diff --git a/contrib/gstyle/gstyle-color-panel.h b/contrib/gstyle/gstyle-color-panel.h
new file mode 100644
index 0000000..ebf643e
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-panel.h
@@ -0,0 +1,79 @@
+/* gstyle-color-panel.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_PANEL_H
+#define GSTYLE_COLOR_PANEL_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-palette.h"
+#include "gstyle-palette-widget.h"
+#include "gstyle-xyz.h"
+
+
+#define GSTYLE_TYPE_COLOR_PANEL_PREFS (gstyle_color_panel_prefs_get_type())
+#define GSTYLE_TYPE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS (gstyle_color_panel_strings_visible_flags_get_type ())
+#define GSTYLE_TYPE_COLOR_PANEL (gstyle_color_panel_get_type())
+
+typedef enum
+{
+ GSTYLE_COLOR_PANEL_PREFS_COMPONENTS,
+ GSTYLE_COLOR_PANEL_PREFS_COLOR_STRINGS,
+ GSTYLE_COLOR_PANEL_PREFS_PALETTES,
+ GSTYLE_COLOR_PANEL_PREFS_PALETTES_LIST
+} GstyleColorPanelPrefs;
+
+typedef enum
+{
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_NONE = 0,
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HEX3 = 1 << 0,
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HEX6 = 1 << 1,
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_RGB = 1 << 2,
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_RGBA = 1 << 3,
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HSL = 1 << 4,
+ GSTYLE_COLOR_PANEL_STRINGS_VISIBLE_FLAGS_HSLA = 1 << 5
+} GstyleColorPanelStringsVisibleFlags;
+
+G_DECLARE_FINAL_TYPE (GstyleColorPanel, gstyle_color_panel, GSTYLE, COLOR_PANEL, GtkBox)
+
+GType gstyle_color_panel_prefs_get_type (void);
+GType gstyle_color_panel_strings_visible_flags_get_type (void);
+
+GstyleColorPanel *gstyle_color_panel_new (void);
+GstylePaletteWidget *gstyle_color_panel_get_palette_widget (GstyleColorPanel *self);
+void gstyle_color_panel_get_rgba (GstyleColorPanel *self,
+ GdkRGBA *rgba);
+void gstyle_color_panel_set_rgba (GstyleColorPanel *self,
+ const GdkRGBA *rgba);
+void gstyle_color_panel_get_xyz (GstyleColorPanel *self,
+ GstyleXYZ *xyz);
+void gstyle_color_panel_set_xyz (GstyleColorPanel *self,
+ const GstyleXYZ *xyz);
+void gstyle_color_panel_set_prefs_pages (GstyleColorPanel *self,
+ GtkWidget
*components_page,
+ GtkWidget
*colorstrings_page,
+ GtkWidget
*palettes_page,
+ GtkWidget
*palettes_list_page);
+void gstyle_color_panel_show_palette (GstyleColorPanel *self,
+ GstylePalette *palette);
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_PANEL_H */
+
diff --git a/contrib/gstyle/gstyle-color-plane.c b/contrib/gstyle/gstyle-color-plane.c
new file mode 100644
index 0000000..93a0bc3
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-plane.c
@@ -0,0 +1,1648 @@
+/* gstyle-color-plane.c
+ *
+ * based on : gtk-color-plane
+ * GTK - The GIMP Toolkit
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-color-plane"
+
+#include <math.h>
+#include <cairo/cairo.h>
+#include <glib/gi18n.h>
+
+#include "gstyle-cielab.h"
+#include "gstyle-color-convert.h"
+#include "gstyle-css-provider.h"
+#include "gstyle-utils.h"
+
+#include "gstyle-color-plane.h"
+
+typedef struct _ComputeData
+{
+ gint width;
+ gint height;
+ gint stride;
+ guint32 *buffer;
+
+ gdouble x_factor;
+ gdouble y_factor;
+ gdouble lab_x_factor;
+ gdouble lab_y_factor;
+ gdouble lab_l_factor;
+} ComputeData;
+
+typedef enum _ColorSpaceId
+{
+ COLOR_SPACE_RGB,
+ COLOR_SPACE_CIELAB,
+ COLOR_SPACE_HSV,
+ COLOR_SPACE_NONE
+} ColorSpaceId;
+
+typedef struct _Component
+{
+ GtkAdjustment *adj;
+ gulong handler;
+ gdouble val;
+ gdouble factor;
+ ColorSpaceId color_space;
+} Component;
+
+typedef struct
+{
+ cairo_surface_t *surface;
+
+ GstyleCssProvider *default_provider;
+
+ GtkGesture *drag_gesture;
+ GtkGesture *long_press_gesture;
+
+ GtkBorder cached_margin;
+ GtkBorder cached_border;
+ GdkRectangle cached_margin_box;
+ GdkRectangle cached_border_box;
+
+ GstyleColorPlaneMode mode;
+ GstyleXYZ xyz;
+ gdouble cursor_x;
+ gdouble cursor_y;
+
+ ComputeData data;
+ GstyleColorFilter filter;
+ gpointer filter_user_data;
+
+ Component comp [N_GSTYLE_COLOR_COMPONENT];
+ GstyleColorComponent ref_comp;
+ GstyleColorUnit preferred_unit;
+} GstyleColorPlanePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GstyleColorPlane, gstyle_color_plane, GTK_TYPE_DRAWING_AREA)
+
+enum {
+ PROP_0,
+ PROP_MODE,
+ PROP_RGBA,
+ PROP_XYZ,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+/* We return %TRUE if there's no changes in border and margin, %FALSE otherwise.*/
+static gboolean
+update_css_boxes (GstyleColorPlane *self)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GtkWidget *widget = GTK_WIDGET (self);
+ GtkStyleContext *style_context;
+ GtkStateFlags state;
+ GdkRectangle margin_box;
+ GdkRectangle border_box;
+ GtkBorder margin;
+ GtkBorder border;
+ gboolean res;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ state = gtk_style_context_get_state (style_context);
+
+ gtk_style_context_get_margin (style_context, state, &margin);
+ gtk_style_context_get_border (style_context, state, &border);
+ gtk_widget_get_allocation (widget, &margin_box);
+ margin_box.x = margin_box.y = 0;
+
+ gstyle_utils_get_rect_resized_box (margin_box, &margin_box, &margin);
+ gstyle_utils_get_rect_resized_box (margin_box, &border_box, &border);
+
+ res = (gstyle_utils_cmp_border (margin, priv->cached_margin) ||
+ gstyle_utils_cmp_border (border, priv->cached_border));
+
+ priv->cached_margin_box = margin_box;
+ priv->cached_border_box = border_box;
+ priv->cached_margin = margin;
+ priv->cached_border = border;
+
+ return res;
+}
+
+static void
+get_xyz_from_cursor (GstyleColorPlane *self,
+ GstyleXYZ *xyz)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gdouble hsv_h, hsv_s, hsv_v;
+ GstyleCielab lab;
+ GdkRGBA rgba = {0};
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+ g_assert (xyz != NULL);
+
+ switch (priv->mode)
+ {
+ case GSTYLE_COLOR_PLANE_MODE_HUE:
+ hsv_h = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val / priv->comp
[GSTYLE_COLOR_COMPONENT_HSV_H].factor,
+ hsv_s = priv->cursor_x * priv->data.x_factor;
+ hsv_v = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
+ gstyle_color_convert_hsv_to_xyz (hsv_h, hsv_s, hsv_v, xyz);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_SATURATION:
+ hsv_s = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].val / priv->comp
[GSTYLE_COLOR_COMPONENT_HSV_S].factor,
+ hsv_h = priv->cursor_x * priv->data.x_factor;
+ hsv_v = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
+ gstyle_color_convert_hsv_to_xyz (hsv_h, hsv_s, hsv_v, xyz);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS:
+ hsv_v = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].val / priv->comp
[GSTYLE_COLOR_COMPONENT_HSV_V].factor,
+ hsv_h = priv->cursor_x * priv->data.x_factor;
+ hsv_s = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
+ gstyle_color_convert_hsv_to_xyz (hsv_h, hsv_s, hsv_v, xyz);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_L:
+ lab.l = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].val / priv->comp
[GSTYLE_COLOR_COMPONENT_LAB_L].factor,
+ lab.a = priv->cursor_x * priv->data.lab_x_factor - 128.0;
+ lab.b = (priv->data.height - priv->cursor_y - 1) * priv->data.lab_y_factor - 128.0;
+ gstyle_color_convert_cielab_to_xyz (&lab, xyz);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_A:
+ lab.a = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].val / priv->comp
[GSTYLE_COLOR_COMPONENT_LAB_A].factor,
+ lab.b = priv->cursor_x * priv->data.lab_x_factor - 128.0;
+ lab.l = (priv->data.height - priv->cursor_y - 1) * priv->data.lab_l_factor;
+ gstyle_color_convert_cielab_to_xyz (&lab, xyz);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_B:
+ lab.b = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].val / priv->comp
[GSTYLE_COLOR_COMPONENT_LAB_B].factor,
+ lab.a = priv->cursor_x * priv->data.lab_x_factor - 128.0;
+ lab.l = (priv->data.height - priv->cursor_y - 1) * priv->data.lab_l_factor;
+ gstyle_color_convert_cielab_to_xyz (&lab, xyz);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_RED:
+ rgba.red = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].val / priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_RED].factor,
+ rgba.blue = priv->cursor_x * priv->data.x_factor;
+ rgba.green = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
+ gstyle_color_convert_rgb_to_xyz (&rgba, xyz);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_GREEN:
+ rgba.green = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].val / priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_GREEN].factor,
+ rgba.blue = priv->cursor_x * priv->data.x_factor;
+ rgba.red = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
+ gstyle_color_convert_rgb_to_xyz (&rgba, xyz);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_BLUE:
+ rgba.blue = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].val / priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_BLUE].factor,
+ rgba.red = priv->cursor_x * priv->data.x_factor;
+ rgba.green = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
+ gstyle_color_convert_rgb_to_xyz (&rgba, xyz);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_NONE:
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+set_cursor_from_xyz (GstyleColorPlane *self,
+ GstyleXYZ *xyz)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gdouble hsv_h, hsv_s, hsv_v;
+ GstyleCielab lab;
+ GdkRGBA rgba = {0};
+ gdouble x, y;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+ g_assert (xyz != NULL);
+
+ switch (priv->mode)
+ {
+ case GSTYLE_COLOR_PLANE_MODE_HUE:
+ gstyle_color_convert_xyz_to_hsv (xyz, &hsv_h, &hsv_s, &hsv_v);
+ x = hsv_s / priv->data.x_factor;
+ y = (1.0 - hsv_v) / priv->data.y_factor;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_SATURATION:
+ gstyle_color_convert_xyz_to_hsv (xyz, &hsv_h, &hsv_s, &hsv_v);
+ x = hsv_h / priv->data.x_factor;
+ y = (1.0 - hsv_v) / priv->data.y_factor;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS:
+ gstyle_color_convert_xyz_to_hsv (xyz, &hsv_h, &hsv_s, &hsv_v);
+ x = hsv_h / priv->data.x_factor;
+ y = (1.0 - hsv_s) / priv->data.y_factor;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_L:
+ gstyle_color_convert_xyz_to_cielab (xyz, &lab);
+ x = (lab.a + 128.0) / priv->data.lab_x_factor;
+ y = (128.0 - lab.b) / priv->data.lab_y_factor;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_A:
+ gstyle_color_convert_xyz_to_cielab (xyz, &lab);
+ x = (lab.b + 128.0) / priv->data.lab_x_factor;
+ y = (100.0 - lab.l) / priv->data.lab_l_factor;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_B:
+ gstyle_color_convert_xyz_to_cielab (xyz, &lab);
+ x = (lab.a + 128.0) / priv->data.lab_x_factor;
+ y = (100.0 - lab.l) / priv->data.lab_y_factor;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_RED:
+ gstyle_color_convert_xyz_to_rgb (xyz, &rgba);
+ x = rgba.blue / priv->data.x_factor;
+ y = (1.0 - rgba.green) / priv->data.y_factor;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_GREEN:
+ gstyle_color_convert_xyz_to_rgb (xyz, &rgba);
+ x = rgba.blue / priv->data.x_factor;
+ y = (1.0 - rgba.red) / priv->data.y_factor;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_BLUE:
+ gstyle_color_convert_xyz_to_rgb (xyz, &rgba);
+ x = rgba.red / priv->data.x_factor;
+ y = (1.0 - rgba.green) / priv->data.y_factor;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_NONE:
+ default:
+ g_assert_not_reached ();
+ }
+
+ priv->cursor_x = CLAMP (x, 0.0, (gdouble)priv->data.width - 1.0);
+ priv->cursor_y = CLAMP (y, 0.0, (gdouble)priv->data.height - 1.0);
+}
+
+static void
+configure_component (GstyleColorPlane *self,
+ GstyleColorComponent comp,
+ gdouble upper,
+ gdouble factor)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gdouble new_value;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+ g_assert (GTK_IS_ADJUSTMENT (priv->comp [comp].adj));
+
+ new_value = priv->comp [comp].val / priv->comp [comp].factor * factor;
+ priv->comp [comp].factor = factor;
+
+ g_object_freeze_notify (G_OBJECT (priv->comp [comp].adj));
+ gtk_adjustment_set_upper (priv->comp [comp].adj, upper);
+ gtk_adjustment_set_value (priv->comp [comp].adj, new_value);
+ g_object_thaw_notify (G_OBJECT (priv->comp [comp].adj));
+}
+
+static void
+setup_component (GstyleColorPlane *self,
+ GstyleColorComponent comp,
+ gdouble origin,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble factor,
+ ColorSpaceId color_space)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ priv->comp [comp].adj = g_object_ref (gtk_adjustment_new (origin, lower, upper, step_increment,
page_increment, 0));
+ priv->comp [comp].factor = factor;
+ priv->comp [comp].color_space = color_space;
+}
+
+/**
+ * gstyle_color_plane_set_preferred_unit:
+ * @self: A #GstyleColorPlane
+ * @preferred_unit: A #GstyleColorUnit enum value
+ *
+ * Set percent or value as the preferred unit for rgb adjustment range.
+ * [0, 100] for percent unit or [0, 255] for value.
+ *
+ */
+void
+gstyle_color_plane_set_preferred_unit (GstyleColorPlane *self,
+ GstyleColorUnit preferred_unit)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gdouble max_range;
+
+ g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
+
+ if (priv->preferred_unit != preferred_unit)
+ {
+ priv->preferred_unit = preferred_unit;
+ if (preferred_unit == GSTYLE_COLOR_UNIT_PERCENT)
+ max_range = 100.0;
+ else if (preferred_unit == GSTYLE_COLOR_UNIT_VALUE)
+ max_range = 255.0;
+ else
+ g_assert_not_reached ();
+
+ configure_component (self, GSTYLE_COLOR_COMPONENT_RGB_RED, max_range, max_range);
+ configure_component (self, GSTYLE_COLOR_COMPONENT_RGB_GREEN, max_range, max_range);
+ configure_component (self, GSTYLE_COLOR_COMPONENT_RGB_BLUE, max_range, max_range);
+ }
+}
+
+/**
+ * gstyle_color_plane_get_filter: (skip):
+ * @self: A #GstyleColorPlane
+ *
+ * Get a pointer to the current filter function or %NULL
+ * if no filter is actually set.
+ *
+ * Returns: (nullable): A GstyleColorFilter function pointer.
+ *
+ */
+GstyleColorFilter
+gstyle_color_plane_get_filter (GstyleColorPlane *self)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_return_val_if_fail (GSTYLE_IS_COLOR_PLANE (self), NULL);
+
+ return priv->filter;
+}
+
+/**
+ * gstyle_color_plane_set_filter:
+ * @self: A #GstyleColorPlane
+ * @filter_cb: (scope notified) (nullable): A GstyleColorFilter filter function or
+ * %NULL to unset the current filter. In this case, user_data is ignored
+ * @user_data: (closure) (nullable): user data to pass when calling the filter function
+ *
+ * Set a filter to be used to change the drawing of the color plane.
+ *
+ */
+void
+gstyle_color_plane_set_filter (GstyleColorPlane *self,
+ GstyleColorFilter filter_cb,
+ gpointer user_data)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
+
+ priv->filter = filter_cb;
+ priv->filter_user_data = (filter_cb == NULL) ? NULL : user_data;
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+compute_plane_hue_mode (GstyleColorPlane *self,
+ ComputeData data)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gdouble hue, saturation, value;
+ GdkRGBA rgba = {0};
+ guint32 *p;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ hue = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].factor;
+ for (gint y = 0; y < data.height; ++y)
+ {
+ value = CLAMP ((data.height - y) * data.y_factor, 0.0, 1.0);
+ p = data.buffer + y * (data.stride / 4);
+ for (gint x = 0; x < data.width; ++x)
+ {
+ saturation = x * data.x_factor;
+ gstyle_color_convert_hsv_to_rgb (hue, saturation, value, &rgba);
+ if (priv->filter != NULL)
+ priv->filter (&rgba, &rgba, priv->filter_user_data);
+
+ p[x] = pack_rgba24 (&rgba);
+ }
+ }
+}
+
+static void
+compute_plane_saturation_mode (GstyleColorPlane *self,
+ ComputeData data)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gdouble hue, saturation, value;
+ GdkRGBA rgba = {0};
+ guint32 *p;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ saturation = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].val / priv->comp
[GSTYLE_COLOR_COMPONENT_HSV_S].factor;
+ for (gint y = 0; y < data.height; ++y)
+ {
+ value = CLAMP ((data.height - y) * data.y_factor, 0.0, 1.0);
+ p = data.buffer + y * (data.stride / 4);
+ for (gint x = 0; x < data.width; ++x)
+ {
+ hue = x * data.x_factor;
+ gstyle_color_convert_hsv_to_rgb (hue, saturation, value, &rgba);
+ if (priv->filter != NULL)
+ priv->filter (&rgba, &rgba, priv->filter_user_data);
+
+ p[x] = pack_rgba24 (&rgba);
+ }
+ }
+}
+
+static void
+compute_plane_brightness_mode (GstyleColorPlane *self,
+ ComputeData data)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gdouble hue, saturation, value;
+ GdkRGBA rgba = {0};
+ guint32 *p;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ value = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].factor;
+ for (gint y = 0; y < data.height - 1; ++y)
+ {
+ saturation = CLAMP ((data.height - y) * data.y_factor, 0.0, 1.0);
+ p = data.buffer + y * (data.stride / 4);
+ for (gint x = 0; x < data.width; ++x)
+ {
+ hue = x * data.x_factor;
+ gstyle_color_convert_hsv_to_rgb (hue, saturation, value, &rgba);
+ if (priv->filter != NULL)
+ priv->filter (&rgba, &rgba, priv->filter_user_data);
+
+ p[x] = pack_rgba24 (&rgba);
+ }
+ }
+}
+
+static void
+compute_plane_cielab_l_mode (GstyleColorPlane *self,
+ ComputeData data)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GstyleCielab lab;
+ GdkRGBA rgba = {0};
+ guint32 *p;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ lab.l = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].factor;
+ for (gint y = 0; y < data.height; ++y)
+ {
+ lab.b = (data.height - y) * data.lab_y_factor - 128.0;
+ p = data.buffer + y * (data.stride / 4);
+ for (gint x = 0; x < data.width; ++x)
+ {
+ lab.a = x * data.lab_x_factor - 128.0;
+ gstyle_color_convert_cielab_to_rgb (&lab, &rgba);
+ if (priv->filter != NULL)
+ priv->filter (&rgba, &rgba, priv->filter_user_data);
+
+ p[x] = pack_rgba24 (&rgba);
+ }
+ }
+}
+
+static void
+compute_plane_cielab_a_mode (GstyleColorPlane *self,
+ ComputeData data)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GstyleCielab lab;
+ GdkRGBA rgba = {0};
+ guint32 *p;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ lab.a = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].factor;
+ for (gint y = 0; y < data.height; ++y)
+ {
+ lab.l = (data.height - y) * data.lab_l_factor;
+ p = data.buffer + y * (data.stride / 4);
+ for (gint x = 0; x < data.width; ++x)
+ {
+ lab.b = x * data.lab_x_factor - 128.0;
+ gstyle_color_convert_cielab_to_rgb (&lab, &rgba);
+ if (priv->filter != NULL)
+ priv->filter (&rgba, &rgba, priv->filter_user_data);
+
+ p[x] = pack_rgba24 (&rgba);
+ }
+ }
+}
+
+static void
+compute_plane_cielab_b_mode (GstyleColorPlane *self,
+ ComputeData data)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GstyleCielab lab;
+ GdkRGBA rgba = {0};
+ guint32 *p;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ lab.b = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].factor;
+ for (gint y = 0; y < data.height; ++y)
+ {
+ lab.l = (data.height - y) * data.lab_l_factor;
+ p = data.buffer + y * (data.stride / 4);
+ for (gint x = 0; x < data.width; ++x)
+ {
+ lab.a = x * data.lab_x_factor - 128.0;
+ gstyle_color_convert_cielab_to_rgb (&lab, &rgba);
+ if (priv->filter != NULL)
+ priv->filter (&rgba, &rgba, priv->filter_user_data);
+
+ p[x] = pack_rgba24 (&rgba);
+ }
+ }
+}
+
+static void
+compute_plane_red_mode (GstyleColorPlane *self,
+ ComputeData data)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GdkRGBA rgba = {0};
+ guint32 *p;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ rgba.red = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].val / priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_RED].factor;
+ for (gint y = 0; y < data.height; ++y)
+ {
+ rgba.green = (data.height - y) * data.y_factor;
+ p = data.buffer + y * (data.stride / 4);
+ for (gint x = 0; x < data.width; ++x)
+ {
+ rgba.blue = x * data.x_factor;
+ if (priv->filter != NULL)
+ priv->filter (&rgba, &rgba, priv->filter_user_data);
+
+ p[x] = pack_rgba24 (&rgba);
+ }
+ }
+}
+
+static void
+compute_plane_green_mode (GstyleColorPlane *self,
+ ComputeData data)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GdkRGBA rgba = {0};
+ guint32 *p;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ rgba.green = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].val / priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_GREEN].factor;
+ for (gint y = 0; y < data.height; ++y)
+ {
+ rgba.red = (data.height - y) * data.y_factor;
+ p = data.buffer + y * (data.stride / 4);
+ for (gint x = 0; x < data.width; ++x)
+ {
+ rgba.blue = x * data.x_factor;
+ if (priv->filter != NULL)
+ priv->filter (&rgba, &rgba, priv->filter_user_data);
+
+ p[x] = pack_rgba24 (&rgba);
+ }
+ }
+}
+
+static void
+compute_plane_blue_mode (GstyleColorPlane *self,
+ ComputeData data)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GdkRGBA rgba = {0};
+ guint32 *p;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ rgba.blue = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].val / priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_BLUE].factor;
+ for (gint y = 0; y < data.height; ++y)
+ {
+ rgba.green = (data.height - y) * data.y_factor;
+ p = data.buffer + y * (data.stride / 4);
+ for (gint x = 0; x < data.width; ++x)
+ {
+ rgba.red = x * data.x_factor;
+ if (priv->filter != NULL)
+ priv->filter (&rgba, &rgba, priv->filter_user_data);
+
+ p[x] = pack_rgba24 (&rgba);
+ }
+ }
+}
+
+static gboolean
+create_surface (GstyleColorPlane *self)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GtkWidget *widget = (GtkWidget *)self;
+ cairo_surface_t *surface;
+ cairo_surface_t *tmp;
+ cairo_t *cr;
+ gint adjusted_height;
+ gint adjusted_width;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ if (!gtk_widget_get_realized (widget))
+ return FALSE;
+
+ /* TODO: keep only one of priv->data.width or priv->cached_border_box.width */
+
+ priv->data.width = priv->cached_border_box.width;
+ priv->data.height = priv->cached_border_box.height;
+ adjusted_height = priv->data.height - 1;
+ adjusted_width = priv->data.width - 1;
+
+ priv->data.y_factor = 1.0 / adjusted_height;
+ priv->data.x_factor = 1.0 / adjusted_width;
+ priv->data.lab_y_factor = 255.0 / adjusted_height;
+ priv->data.lab_x_factor = 255.0 / adjusted_width;
+ priv->data.lab_l_factor = 100.0 / adjusted_height;
+
+ surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
+ CAIRO_CONTENT_COLOR,
+ priv->data.width, priv->data.height);
+
+ if (priv->surface)
+ cairo_surface_destroy (priv->surface);
+
+ priv->surface = surface;
+
+ if (priv->data.width <= 1 || priv->data.height <= 1)
+ return FALSE;
+
+ priv->data.stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, priv->data.width);
+ priv->data.buffer = g_malloc (priv->data.height * priv->data.stride);
+
+ switch (priv->mode)
+ {
+ case GSTYLE_COLOR_PLANE_MODE_HUE:
+ compute_plane_hue_mode (self, priv->data);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_SATURATION:
+ compute_plane_saturation_mode (self, priv->data);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS:
+ compute_plane_brightness_mode (self, priv->data);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_L:
+ compute_plane_cielab_l_mode (self, priv->data);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_A:
+ compute_plane_cielab_a_mode (self, priv->data);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_B:
+ compute_plane_cielab_b_mode (self, priv->data);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_RED:
+ compute_plane_red_mode (self, priv->data);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_GREEN:
+ compute_plane_green_mode (self, priv->data);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_BLUE:
+ compute_plane_blue_mode (self, priv->data);
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_NONE:
+ default:
+ g_assert_not_reached ();
+ }
+
+ tmp = cairo_image_surface_create_for_data ((guchar *)priv->data.buffer, CAIRO_FORMAT_RGB24,
+ priv->data.width, priv->data.height, priv->data.stride);
+ cr = cairo_create (surface);
+ cairo_set_source_surface (cr, tmp, 0, 0);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (tmp);
+ g_free (priv->data.buffer);
+
+ return TRUE;
+}
+
+static gboolean
+gstyle_color_plane_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ GstyleColorPlane *self = (GstyleColorPlane *)widget;
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gint left_spacing;
+ gint top_spacing;
+ gint x, y;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+ g_assert (cr != NULL);
+
+ if (!gtk_widget_get_visible (widget))
+ return GDK_EVENT_PROPAGATE;
+
+ if (update_css_boxes (self) || priv->surface == NULL)
+ create_surface (self);
+
+ left_spacing = priv->cached_margin.left + priv->cached_border.left;
+ top_spacing = priv->cached_margin.top + priv->cached_border.top;
+ x = round (priv->cursor_x) + left_spacing;
+ y = round (priv->cursor_y) + top_spacing;
+
+ cairo_set_source_surface (cr, priv->surface, priv->cached_border_box.x, priv->cached_border_box.y);
+ cairo_paint (cr);
+
+ gtk_render_frame (gtk_widget_get_style_context (widget), cr,
+ priv->cached_margin_box.x, priv->cached_margin_box.y,
+ priv->cached_margin_box.width, priv->cached_margin_box.height);
+
+ cairo_move_to (cr, left_spacing, y + 0.5);
+ cairo_line_to (cr, left_spacing + priv->cached_border_box.width, y + 0.5);
+
+ cairo_move_to (cr, x + 0.5, top_spacing);
+ cairo_line_to (cr, x + 0.5, top_spacing + priv->cached_border_box.height);
+
+ if (gtk_widget_has_visible_focus (widget))
+ {
+ cairo_set_line_width (cr, 3.0);
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6);
+ cairo_stroke_preserve (cr);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8);
+ cairo_stroke (cr);
+ }
+ else
+ {
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_source_rgba (cr, 0.8, 0.8, 0.8, 0.8);
+ cairo_stroke (cr);
+ }
+
+ return FALSE;
+}
+
+static inline gboolean
+compare_xyz (GstyleXYZ xyz1,
+ GstyleXYZ xyz2)
+{
+ return (xyz1.x == xyz2.x &&
+ xyz1.y == xyz2.y &&
+ xyz1.z == xyz2.z &&
+ xyz1.alpha == xyz2.alpha);
+}
+
+/* Adjustments are updated from the stored xyz */
+static void
+update_adjustments (GstyleColorPlane *self,
+ GstyleXYZ *xyz,
+ GstyleColorComponent changed_comp)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gdouble hue, saturation, value;
+ GstyleCielab lab;
+ ColorSpaceId color_space;
+ GdkRGBA rgba = {0};
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+ g_assert (xyz != NULL);
+
+ if (!compare_xyz (priv->xyz, *xyz))
+ {
+ color_space = (changed_comp == GSTYLE_COLOR_COMPONENT_NONE) ? COLOR_SPACE_NONE : priv->comp
[changed_comp].color_space;
+ if (color_space != COLOR_SPACE_RGB)
+ {
+ gstyle_color_convert_xyz_to_rgb (xyz, &rgba);
+ priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].val = rgba.red * priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_RED].factor;
+ priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].val = rgba.green * priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_GREEN].factor;
+ priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].val = rgba.blue * priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_BLUE].factor;
+ }
+
+ if (color_space != COLOR_SPACE_CIELAB)
+ {
+ gstyle_color_convert_xyz_to_cielab (xyz, &lab);
+ priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].val = lab.l * priv->comp
[GSTYLE_COLOR_COMPONENT_LAB_L].factor;
+ priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].val = lab.a * priv->comp
[GSTYLE_COLOR_COMPONENT_LAB_A].factor;
+ priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].val = lab.b * priv->comp
[GSTYLE_COLOR_COMPONENT_LAB_B].factor;
+ }
+
+ if (color_space != COLOR_SPACE_HSV)
+ {
+ gstyle_color_convert_xyz_to_hsv (xyz, &hue, &saturation, &value);
+ priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val = hue * priv->comp
[GSTYLE_COLOR_COMPONENT_HSV_H].factor;
+ priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].val = saturation * priv->comp
[GSTYLE_COLOR_COMPONENT_HSV_S].factor;
+ priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].val = value * priv->comp
[GSTYLE_COLOR_COMPONENT_HSV_V].factor;
+ }
+
+ for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
+ if (priv->comp [i].color_space != color_space)
+ {
+ g_signal_handler_block (priv->comp [i].adj, priv->comp [i].handler);
+ gtk_adjustment_set_value (priv->comp [i].adj, priv->comp [i].val);
+ g_signal_handler_unblock (priv->comp [i].adj, priv->comp [i].handler);
+ }
+ }
+}
+
+static void
+update_surface_and_cursor (GstyleColorPlane *self,
+ gboolean update_surface)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ if (update_surface)
+ create_surface (self);
+
+ set_cursor_from_xyz (self, &priv->xyz);
+
+ if (gtk_widget_get_realized (GTK_WIDGET (self)))
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+update_cursor (GstyleColorPlane *self,
+ gdouble x,
+ gdouble y)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gint left_spacing;
+ gint top_spacing;
+ GstyleXYZ xyz;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ left_spacing = priv->cached_margin.left + priv->cached_border.left;
+ top_spacing = priv->cached_margin.top + priv->cached_border.top;
+ x = CLAMP (x - left_spacing, 0.0, priv->data.width - 1.0);
+ y = CLAMP (y - top_spacing, 0.0, priv->data.height - 1.0);
+
+ if (priv->cursor_x != x || priv->cursor_y != y)
+ {
+ priv->cursor_x = x;
+ priv->cursor_y = y;
+
+ get_xyz_from_cursor (self, &xyz);
+ update_adjustments (self, &xyz, GSTYLE_COLOR_COMPONENT_NONE);
+ priv->xyz = xyz;
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RGBA]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_XYZ]);
+ }
+}
+
+static void
+move_cursor (GstyleColorPlane *self,
+ gdouble step_x,
+ gdouble step_y)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (self)))
+ return;
+
+ update_cursor (self, priv->cursor_x + step_x, priv->cursor_y - step_y);
+ /* TODO: ring when reaching the border */
+}
+
+static GstyleColorComponent
+get_adj_id (GstyleColorPlane *self,
+ GtkAdjustment *adj)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+ g_assert (GTK_IS_ADJUSTMENT (adj));
+
+ for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
+ if (adj == priv->comp [i].adj)
+ return i;
+
+ g_assert_not_reached ();
+}
+
+static void
+adjustments_changed (GstyleColorPlane *self,
+ GtkAdjustment *adj)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gdouble hue, saturation, value;
+ GdkRGBA rgba;
+ GstyleXYZ xyz;
+ GstyleCielab lab;
+ GstyleColorComponent changed_comp;
+ gdouble old_ref_val;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+ g_assert (GTK_IS_ADJUSTMENT (adj));
+
+ old_ref_val = priv->comp [priv->ref_comp].val;
+ changed_comp = get_adj_id (self, adj);
+ priv->comp [changed_comp].val = gtk_adjustment_get_value (priv->comp [changed_comp].adj);
+
+ if (changed_comp == GSTYLE_COLOR_COMPONENT_HSV_H ||
+ changed_comp == GSTYLE_COLOR_COMPONENT_HSV_S ||
+ changed_comp == GSTYLE_COLOR_COMPONENT_HSV_V)
+ {
+ hue = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].factor;
+ saturation = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].val / priv->comp
[GSTYLE_COLOR_COMPONENT_HSV_S].factor;
+ value = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].val / priv->comp
[GSTYLE_COLOR_COMPONENT_HSV_V].factor;
+
+ gstyle_color_convert_hsv_to_xyz (hue, saturation, value, &xyz);
+ }
+ else if (changed_comp == GSTYLE_COLOR_COMPONENT_LAB_L ||
+ changed_comp == GSTYLE_COLOR_COMPONENT_LAB_A ||
+ changed_comp == GSTYLE_COLOR_COMPONENT_LAB_B)
+ {
+ lab.l = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].val / priv->comp
[GSTYLE_COLOR_COMPONENT_LAB_L].factor;
+ lab.a = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].val / priv->comp
[GSTYLE_COLOR_COMPONENT_LAB_A].factor;
+ lab.b = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].val / priv->comp
[GSTYLE_COLOR_COMPONENT_LAB_B].factor;
+
+ gstyle_color_convert_cielab_to_xyz (&lab, &xyz);
+ }
+ else
+ {
+ rgba.red = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].val / priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_RED].factor;
+ rgba.green = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].val / priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_GREEN].factor;
+ rgba.blue = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].val / priv->comp
[GSTYLE_COLOR_COMPONENT_RGB_BLUE].factor;
+ gstyle_color_convert_rgb_to_xyz (&rgba, &xyz);
+ }
+
+ xyz.alpha = 1;
+ update_adjustments (self, &xyz, changed_comp);
+ priv->xyz = xyz;
+ update_surface_and_cursor (self, old_ref_val != priv->comp [priv->ref_comp].val);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RGBA]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_XYZ]);
+}
+
+/**
+ * gstyle_color_plane_new:
+ *
+ * Returns: a new #GstyleColorPlane
+ */
+GstyleColorPlane *
+gstyle_color_plane_new (void)
+{
+ return g_object_new (GSTYLE_TYPE_COLOR_PLANE, NULL);
+}
+
+static void
+gstyle_color_plane_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GstyleColorPlane *self = GSTYLE_COLOR_PLANE (widget);
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ GTK_WIDGET_CLASS (gstyle_color_plane_parent_class)->size_allocate (widget, allocation);
+
+ update_css_boxes (self);
+
+ if (create_surface (GSTYLE_COLOR_PLANE (widget)))
+ set_cursor_from_xyz (self, &priv->xyz);
+}
+
+static gboolean
+gstyle_color_plane_key_press (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ GstyleColorPlane *self = GSTYLE_COLOR_PLANE (widget);
+ gdouble step;
+
+ g_assert (event != NULL);
+
+ if ((event->state & GDK_MOD1_MASK) != 0)
+ step = 0.1;
+ else
+ step = 0.01;
+
+ if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
+ move_cursor (self, 0, step);
+ else if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
+ move_cursor (self, 0, -step);
+ else if (event->keyval == GDK_KEY_Left || event->keyval == GDK_KEY_KP_Left)
+ move_cursor (self, -step, 0);
+ else if (event->keyval == GDK_KEY_Right || event->keyval == GDK_KEY_KP_Right)
+ move_cursor (self, step, 0);
+ else
+ return GTK_WIDGET_CLASS (gstyle_color_plane_parent_class)->key_press_event (widget, event);
+
+ return TRUE;
+}
+
+/**
+ * gstyle_color_plane_get_xyz:
+ * @self: A #GstyleColorPlane
+ * @xyz: (out): A #GstyleXYZ adress
+ *
+ * Fill @xyz with value at cursor position.
+ * The alpha component is always equal to 1.
+ *
+ */
+void
+gstyle_color_plane_get_xyz (GstyleColorPlane *self,
+ GstyleXYZ *xyz)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
+ g_return_if_fail (xyz != NULL);
+
+ *xyz = priv->xyz;
+}
+
+/**
+ * gstyle_color_plane_get_rgba:
+ * @self: A #GstyleColorPlane
+ * @rgba: (out): A #GdkRGBA adress
+ *
+ * Fill @rgba with value at cursor position.
+ * The alpha component is always equal to 1.
+ *
+ */
+void
+gstyle_color_plane_get_rgba (GstyleColorPlane *self,
+ GdkRGBA *rgba)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
+ g_return_if_fail (rgba != NULL);
+
+ gstyle_color_convert_xyz_to_rgb (&priv->xyz, rgba);
+}
+
+/**
+ * gstyle_color_plane_get_filtered_rgba:
+ * @self: A #GstyleColorPlane
+ * @rgba: (out): A #GdkRGBA adress
+ *
+ * Fill @rgba with filtered value at cursor position.
+ *
+ */
+void
+gstyle_color_plane_get_filtered_rgba (GstyleColorPlane *self,
+ GdkRGBA *rgba)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
+ g_return_if_fail (rgba != NULL);
+
+ gstyle_color_convert_xyz_to_rgb (&priv->xyz, rgba);
+ priv->filter (rgba, rgba, priv->filter_user_data);
+}
+
+/**
+ * gstyle_color_plane_get_component_adjustment:
+ * @self: A #GstyleColorPlane
+ * @comp: A #GstyleColorComponent enum value
+ *
+ * Return the color component adjustment designated by
+ * the #GstyleColorComponent value.
+ *
+ * Returns: (transfer none): #GtkAdjustment.
+ *
+ */
+GtkAdjustment *
+gstyle_color_plane_get_component_adjustment (GstyleColorPlane *self,
+ GstyleColorComponent comp)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_return_val_if_fail (GSTYLE_IS_COLOR_PLANE (self), NULL);
+ g_return_val_if_fail (comp != GSTYLE_COLOR_COMPONENT_NONE, NULL);
+
+ return priv->comp [comp].adj;
+}
+
+/**
+ * gstyle_color_plane_set_rgba:
+ * @self: A #GstyleColorPlane
+ * @rgba: A #GdkRGBA
+ *
+ * Set cursor position from @rgba value.
+ *
+ */
+void
+gstyle_color_plane_set_rgba (GstyleColorPlane *self,
+ const GdkRGBA *rgba)
+{
+ GstyleColorPlanePrivate *priv;
+ GstyleXYZ xyz;
+
+ g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
+ g_return_if_fail (rgba != NULL);
+
+ priv = gstyle_color_plane_get_instance_private (self);
+
+ gstyle_color_convert_rgb_to_xyz ((GdkRGBA *)rgba, &xyz);
+ if (compare_xyz (xyz, priv->xyz))
+ return;
+
+ update_adjustments (self, &xyz, GSTYLE_COLOR_COMPONENT_NONE);
+ priv->xyz = xyz;
+ update_surface_and_cursor (self, TRUE);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RGBA]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_XYZ]);
+}
+
+/**
+ * gstyle_color_plane_set_xyz:
+ * @self: A #GstyleColorPlane.
+ * @xyz: A #GstyleXYZ struct.
+ *
+ * Set cursor position from @rgba value.
+ *
+ */
+void
+gstyle_color_plane_set_xyz (GstyleColorPlane *self,
+ const GstyleXYZ *xyz)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
+ g_return_if_fail (xyz != NULL);
+
+ if (compare_xyz (*xyz, priv->xyz))
+ return;
+
+ update_adjustments (self, (GstyleXYZ *)xyz, GSTYLE_COLOR_COMPONENT_NONE);
+ priv->xyz = *xyz;
+ update_surface_and_cursor (self, TRUE);
+
+ /* TODO: add xyz props */
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RGBA]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_XYZ]);
+}
+
+/**
+ * gstyle_color_plane_set_mode:
+ * @self: a #GstyleColorPlane.
+ * @mode: a #GstylewColorPlaneMode.
+ *
+ * Set the displayed mode to use.
+ *
+ */
+void
+gstyle_color_plane_set_mode (GstyleColorPlane *self,
+ GstyleColorPlaneMode mode)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ gdouble hsv_h, hsv_s, hsv_v;
+ gdouble ref_val;
+ GstyleCielab lab;
+ GdkRGBA rgba = {0};
+
+ g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
+
+ if (priv->mode != mode)
+ {
+ priv->mode = mode;
+
+ switch (priv->mode)
+ {
+ case GSTYLE_COLOR_PLANE_MODE_HUE:
+ gstyle_color_convert_xyz_to_hsv (&priv->xyz, &hsv_h, &hsv_s, &hsv_v);
+ priv->ref_comp = GSTYLE_COLOR_COMPONENT_HSV_H;
+ ref_val = hsv_h;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_SATURATION:
+ gstyle_color_convert_xyz_to_hsv (&priv->xyz, &hsv_h, &hsv_s, &hsv_v);
+ priv->ref_comp = GSTYLE_COLOR_COMPONENT_HSV_S;
+ ref_val = hsv_s;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS:
+ gstyle_color_convert_xyz_to_hsv (&priv->xyz, &hsv_h, &hsv_s, &hsv_v);
+ priv->ref_comp = GSTYLE_COLOR_COMPONENT_HSV_V;
+ ref_val = hsv_v;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_L:
+ gstyle_color_convert_xyz_to_cielab (&priv->xyz, &lab);
+ priv->ref_comp = GSTYLE_COLOR_COMPONENT_LAB_L;
+ ref_val = lab.l;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_A:
+ gstyle_color_convert_xyz_to_cielab (&priv->xyz, &lab);
+ priv->ref_comp = GSTYLE_COLOR_COMPONENT_LAB_A;
+ ref_val = lab.a;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_CIELAB_B:
+ gstyle_color_convert_xyz_to_cielab (&priv->xyz, &lab);
+ priv->ref_comp = GSTYLE_COLOR_COMPONENT_LAB_B;
+ ref_val = lab.b;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_RED:
+ gstyle_color_convert_xyz_to_rgb (&priv->xyz, &rgba);
+ priv->ref_comp = GSTYLE_COLOR_COMPONENT_RGB_RED;
+ ref_val = rgba.red;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_GREEN:
+ gstyle_color_convert_xyz_to_rgb (&priv->xyz, &rgba);
+ priv->ref_comp = GSTYLE_COLOR_COMPONENT_RGB_GREEN;
+ ref_val = rgba.green;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_BLUE:
+ gstyle_color_convert_xyz_to_rgb (&priv->xyz, &rgba);
+ priv->ref_comp = GSTYLE_COLOR_COMPONENT_RGB_BLUE;
+ ref_val = rgba.blue;
+ break;
+
+ case GSTYLE_COLOR_PLANE_MODE_NONE:
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_signal_handler_block (priv->comp [priv->ref_comp].adj, priv->comp [priv->ref_comp].handler);
+
+ priv->comp [priv->ref_comp].val = ref_val * priv->comp [priv->ref_comp].factor;
+ gtk_adjustment_set_value (priv->comp [priv->ref_comp].adj, priv->comp [priv->ref_comp].val);
+
+ g_signal_handler_unblock (priv->comp [priv->ref_comp].adj, priv->comp [priv->ref_comp].handler);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODE]);
+ update_surface_and_cursor (self, TRUE);
+ }
+}
+
+static void
+gstyle_color_plane_finalize (GObject *object)
+{
+ GstyleColorPlane *self = (GstyleColorPlane *)object;
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+
+ if (priv->surface)
+ cairo_surface_destroy (priv->surface);
+
+ g_clear_object (&priv->drag_gesture);
+ g_clear_object (&priv->long_press_gesture);
+ g_clear_object (&priv->default_provider);
+
+ for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
+ g_clear_object (&priv->comp [i].adj);
+
+ G_OBJECT_CLASS (gstyle_color_plane_parent_class)->finalize (object);
+}
+
+static void
+gstyle_color_plane_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleColorPlane *self = GSTYLE_COLOR_PLANE (object);
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GdkRGBA rgba = {0};
+ GstyleXYZ xyz;
+
+ switch (prop_id)
+ {
+ case PROP_MODE:
+ g_value_set_enum (value, priv->mode);
+ break;
+
+ case PROP_RGBA:
+ gstyle_color_plane_get_rgba (self, &rgba);
+ g_value_set_boxed (value, &rgba);
+ break;
+
+ case PROP_XYZ:
+ gstyle_color_plane_get_xyz (self, &xyz);
+ g_value_set_boxed (value, &xyz);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_color_plane_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleColorPlane *self = GSTYLE_COLOR_PLANE (object);
+ GdkRGBA *rgba_p;
+ GstyleXYZ *xyz_p;
+ GstyleXYZ xyz = {0};
+ GdkRGBA rgba = {0.5, 0.3, 0.3, 0.0};
+
+ switch (prop_id)
+ {
+ case PROP_MODE:
+ gstyle_color_plane_set_mode (self, g_value_get_enum (value));
+ break;
+
+ case PROP_RGBA:
+ rgba_p = (GdkRGBA *)g_value_get_boxed (value);
+ if (rgba_p == NULL)
+ rgba_p = &rgba;
+
+ gstyle_color_plane_set_rgba (self, rgba_p);
+
+ case PROP_XYZ:
+ xyz_p = (GstyleXYZ *)g_value_get_boxed (value);
+ if (xyz_p == NULL)
+ {
+ gstyle_color_convert_rgb_to_xyz (&rgba, &xyz);
+ gstyle_color_plane_set_xyz (self, &xyz);
+ }
+ else
+ gstyle_color_plane_set_xyz (self, xyz_p);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_color_plane_class_init (GstyleColorPlaneClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gstyle_color_plane_finalize;
+ object_class->get_property = gstyle_color_plane_get_property;
+ object_class->set_property = gstyle_color_plane_set_property;
+
+ widget_class->draw = gstyle_color_plane_draw;
+ widget_class->size_allocate = gstyle_color_plane_size_allocate;
+ widget_class->key_press_event = gstyle_color_plane_key_press;
+
+ properties [PROP_MODE] =
+ g_param_spec_enum ("mode",
+ "Mode",
+ "The mode displayed",
+ GSTYLE_TYPE_COLOR_PLANE_MODE,
+ GSTYLE_COLOR_PLANE_MODE_HUE,
+ (G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_RGBA] =
+ g_param_spec_boxed ("rgba",
+ "rgba",
+ "Color pointed by the cursor",
+ GDK_TYPE_RGBA,
+ (G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_XYZ] =
+ g_param_spec_boxed ("xyz",
+ "xyz",
+ "Color pointed by the cursor",
+ GSTYLE_TYPE_XYZ,
+ (G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ /* TODO: add *_adjustment properties */
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_css_name (widget_class, "gstylecolorplane");
+}
+
+static void
+set_cross_cursor (GtkWidget *widget,
+ gboolean enabled)
+{
+ GstyleColorPlane *self = (GstyleColorPlane *)widget;
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GdkCursor *cursor = NULL;
+ GdkWindow *window;
+ GdkDevice *device;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ window = gtk_widget_get_window (widget);
+ device = gtk_gesture_get_device (priv->drag_gesture);
+
+ if (!window || !device)
+ return;
+
+ if (enabled)
+ cursor = gdk_cursor_new_from_name (gtk_widget_get_display (GTK_WIDGET (widget)), "crosshair");
+
+ gdk_window_set_device_cursor (window, device, cursor);
+
+ if (cursor)
+ g_object_unref (cursor);
+}
+
+static void
+hold_action (GtkGestureLongPress *gesture,
+ gdouble x,
+ gdouble y,
+ GstyleColorPlane *self)
+{
+ gboolean handled;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ g_signal_emit_by_name (self, "popup-menu", &handled);
+}
+
+static void
+drag_gesture_begin (GtkGestureDrag *gesture,
+ gdouble start_x,
+ gdouble start_y,
+ GstyleColorPlane *self)
+{
+ /* GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self); */
+ guint button;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+ if (button == GDK_BUTTON_SECONDARY)
+ {
+ gboolean handled;
+ g_signal_emit_by_name (self, "popup-menu", &handled);
+ }
+
+ if (button != GDK_BUTTON_PRIMARY)
+ {
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+ return;
+ }
+
+ set_cross_cursor (GTK_WIDGET (self), TRUE);
+ update_cursor (self, start_x, start_y);
+
+ gtk_widget_grab_focus (GTK_WIDGET (self));
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+}
+
+static void
+drag_gesture_update (GtkGestureDrag *gesture,
+ gdouble offset_x,
+ gdouble offset_y,
+ GstyleColorPlane *self)
+{
+ /* GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self); */
+ gdouble start_x, start_y;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (gesture),
+ &start_x, &start_y);
+
+ update_cursor (self, start_x + offset_x, start_y + offset_y);
+}
+
+static void
+drag_gesture_end (GtkGestureDrag *gesture,
+ gdouble offset_x,
+ gdouble offset_y,
+ GstyleColorPlane *self)
+{
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ set_cross_cursor (GTK_WIDGET (self), FALSE);
+}
+
+static void
+gstyle_color_plane_init (GstyleColorPlane *self)
+{
+ GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
+ GtkStyleContext *context;
+ AtkObject *atk_obj;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+
+ gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
+ gtk_widget_set_events (GTK_WIDGET (self), GDK_KEY_PRESS_MASK |
+ GDK_TOUCH_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK);
+
+ atk_obj = gtk_widget_get_accessible (GTK_WIDGET (self));
+ if (GTK_IS_ACCESSIBLE (atk_obj))
+ {
+ atk_object_set_name (atk_obj, _("Color Plane"));
+ atk_object_set_role (atk_obj, ATK_ROLE_COLOR_CHOOSER);
+ }
+
+ setup_component (self, GSTYLE_COLOR_COMPONENT_HSV_H, 0.0, 0.0, 360.0, 1.0, 1.0, 360.0, COLOR_SPACE_HSV);
+ setup_component (self, GSTYLE_COLOR_COMPONENT_HSV_S, 0.0, 0.0, 100.0, 1.0, 1.0, 100.0, COLOR_SPACE_HSV);
+ setup_component (self, GSTYLE_COLOR_COMPONENT_HSV_V, 0.0, 0.0, 100.0, 1.0, 1.0, 100.0, COLOR_SPACE_HSV);
+
+ setup_component (self, GSTYLE_COLOR_COMPONENT_LAB_L, 0.0, 0.0, 100.0, 1.0, 1.0, 1.0, COLOR_SPACE_CIELAB);
+ setup_component (self, GSTYLE_COLOR_COMPONENT_LAB_A, 0.0, -128.0, 128.0, 1.0, 1.0, 1.0,
COLOR_SPACE_CIELAB);
+ setup_component (self, GSTYLE_COLOR_COMPONENT_LAB_B, 0.0, -128.0, 128.0, 1.0, 1.0, 1.0,
COLOR_SPACE_CIELAB);
+
+ setup_component (self, GSTYLE_COLOR_COMPONENT_RGB_RED, 0.0, 0.0, 255.0, 1.0, 1.0, 255.0, COLOR_SPACE_RGB);
+ setup_component (self, GSTYLE_COLOR_COMPONENT_RGB_GREEN, 0.0, 0.0, 255.0, 1.0, 1.0, 255.0,
COLOR_SPACE_RGB);
+ setup_component (self, GSTYLE_COLOR_COMPONENT_RGB_BLUE, 0.0, 0.0, 255.0, 1.0, 1.0, 255.0, COLOR_SPACE_RGB);
+
+ priv->preferred_unit = GSTYLE_COLOR_UNIT_VALUE;
+
+ for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
+ priv->comp [i].handler = g_signal_connect_swapped (priv->comp [i].adj,
+ "value-changed",
+ G_CALLBACK (adjustments_changed), self);
+
+ priv->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (self));
+ g_signal_connect (priv->drag_gesture, "drag-begin", G_CALLBACK (drag_gesture_begin), self);
+ g_signal_connect (priv->drag_gesture, "drag-update", G_CALLBACK (drag_gesture_update), self);
+ g_signal_connect (priv->drag_gesture, "drag-end", G_CALLBACK (drag_gesture_end), self);
+
+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->drag_gesture), 0);
+
+ priv->long_press_gesture = gtk_gesture_long_press_new (GTK_WIDGET (self));
+ g_signal_connect (priv->long_press_gesture, "pressed", G_CALLBACK (hold_action), self);
+
+ gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->long_press_gesture), TRUE);
+
+ priv->mode = GSTYLE_COLOR_PLANE_MODE_HUE;
+ priv->ref_comp = GSTYLE_COLOR_COMPONENT_HSV_H;
+ priv->xyz.alpha = 1;
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ priv->default_provider = gstyle_css_provider_init_default (gtk_style_context_get_screen (context));
+}
+
+GType
+gstyle_color_plane_mode_get_type (void)
+{
+ static GType type_id;
+ static const GEnumValue values[] = {
+ { GSTYLE_COLOR_PLANE_MODE_HUE, "GSTYLE_COLOR_PLANE_MODE_HUE", "hue" },
+ { GSTYLE_COLOR_PLANE_MODE_SATURATION, "GSTYLE_COLOR_PLANE_MODE_SATURATION", "saturation" },
+ { GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS, "GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS", "brightness" },
+ { GSTYLE_COLOR_PLANE_MODE_CIELAB_L, "GSTYLE_COLOR_PLANE_MODE_CIELAB_L", "cielab-l" },
+ { GSTYLE_COLOR_PLANE_MODE_CIELAB_A, "GSTYLE_COLOR_PLANE_MODE_CIELAB_A", "cielab-a" },
+ { GSTYLE_COLOR_PLANE_MODE_CIELAB_B, "GSTYLE_COLOR_PLANE_MODE_CIELAB_B", "cielab-b" },
+ { GSTYLE_COLOR_PLANE_MODE_RED, "GSTYLE_COLOR_PLANE_MODE_RED", "red" },
+ { GSTYLE_COLOR_PLANE_MODE_GREEN, "GSTYLE_COLOR_PLANE_MODE_GREEN", "green" },
+ { GSTYLE_COLOR_PLANE_MODE_BLUE, "GSTYLE_COLOR_PLANE_MODE_BLUE", "blue" },
+ { 0 }
+ };
+
+ if (g_once_init_enter (&type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_enum_register_static ("GstyleColorPlaneMode", values);
+ g_once_init_leave (&type_id, _type_id);
+ }
+
+ return type_id;
+}
diff --git a/contrib/gstyle/gstyle-color-plane.h b/contrib/gstyle/gstyle-color-plane.h
new file mode 100644
index 0000000..01d2559
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-plane.h
@@ -0,0 +1,89 @@
+/* gstyle-color-plane.h
+ *
+ * based on : gtk-color-plane
+ * GTK - The GIMP Toolkit
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_PLANE_H
+#define GSTYLE_COLOR_PLANE_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "gstyle-color.h"
+#include "gstyle-color-component.h"
+#include "gstyle-color-convert.h"
+#include "gstyle-color-filter.h"
+#include "gstyle-xyz.h"
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_COLOR_PLANE (gstyle_color_plane_get_type())
+#define GSTYLE_TYPE_COLOR_PLANE_MODE (gstyle_color_plane_mode_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (GstyleColorPlane, gstyle_color_plane, GSTYLE, COLOR_PLANE, GtkDrawingArea)
+
+typedef enum
+{
+ GSTYLE_COLOR_PLANE_MODE_HUE,
+ GSTYLE_COLOR_PLANE_MODE_SATURATION,
+ GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS,
+ GSTYLE_COLOR_PLANE_MODE_CIELAB_L,
+ GSTYLE_COLOR_PLANE_MODE_CIELAB_A,
+ GSTYLE_COLOR_PLANE_MODE_CIELAB_B,
+ GSTYLE_COLOR_PLANE_MODE_RED,
+ GSTYLE_COLOR_PLANE_MODE_GREEN,
+ GSTYLE_COLOR_PLANE_MODE_BLUE,
+ GSTYLE_COLOR_PLANE_MODE_NONE
+} GstyleColorPlaneMode;
+
+struct _GstyleColorPlaneClass
+{
+ GtkDrawingAreaClass parent;
+};
+
+GType gstyle_color_plane_mode_get_type (void);
+
+GstyleColorPlane *gstyle_color_plane_new (void);
+GtkAdjustment *gstyle_color_plane_get_component_adjustment (GstyleColorPlane *self,
+ GstyleColorComponent comp);
+GstyleColorFilter gstyle_color_plane_get_filter (GstyleColorPlane *self);
+void gstyle_color_plane_get_filtered_rgba (GstyleColorPlane *self,
+ GdkRGBA *rgba);
+void gstyle_color_plane_get_rgba (GstyleColorPlane *self,
+ GdkRGBA *rgba);
+void gstyle_color_plane_get_xyz (GstyleColorPlane *self,
+ GstyleXYZ *xyz);
+void gstyle_color_plane_set_filter (GstyleColorPlane *self,
+ GstyleColorFilter filter_cb,
+ gpointer user_data);
+void gstyle_color_plane_set_mode (GstyleColorPlane *self,
+ GstyleColorPlaneMode mode);
+void gstyle_color_plane_set_preferred_unit (GstyleColorPlane *self,
+ GstyleColorUnit
preferred_unit);
+void gstyle_color_plane_set_rgba (GstyleColorPlane *self,
+ const GdkRGBA *rgba);
+void gstyle_color_plane_set_xyz (GstyleColorPlane *self,
+ const GstyleXYZ *xyz);
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_PLANE_H */
+
diff --git a/contrib/gstyle/gstyle-color-predefined.h b/contrib/gstyle/gstyle-color-predefined.h
new file mode 100644
index 0000000..6573d77
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-predefined.h
@@ -0,0 +1,189 @@
+/* gstyle-color-predefined.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_PREDEFINED_H
+#define GSTYLE_COLOR_PREDEFINED_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ gchar *name;
+ guint8 red;
+ guint8 green;
+ guint8 blue;
+ guint8 index;
+} NamedColor;
+
+G_GNUC_UNUSED
+static NamedColor predefined_colors_table[] =
+{
+ { "aliceblue", 240, 248, 255 },
+ { "antiquewhite", 250, 235, 215 },
+ { "aqua", 0, 255, 255 },
+ { "aquamarine", 127, 255, 212 },
+ { "azure", 240, 255, 255 },
+ { "beige", 245, 245, 220 },
+ { "bisque", 255, 228, 196 },
+ { "black", 0, 0, 0 },
+ { "blanchedalmond", 255, 235, 205 },
+ { "blue", 0, 0, 255 },
+ { "blueviolet", 138, 43, 226 },
+ { "brown", 165, 42, 42 },
+ { "burlywood", 222, 184, 135 },
+ { "cadetblue", 95, 158, 160 },
+ { "chartreuse", 127, 255, 0 },
+ { "chocolate", 210, 105, 30 },
+ { "coral", 255, 127, 80 },
+ { "cornflowerblue", 100, 149, 237 },
+ { "cornsilk", 255, 248, 220 },
+ { "crimson", 220, 20, 60 },
+ { "cyan", 0, 255, 255 },
+ { "darkblue", 0, 0, 139 },
+ { "darkcyan", 0, 139, 139 },
+ { "darkgoldenrod", 184, 134, 11 },
+ { "darkgray", 169, 169, 169 },
+ { "darkgreen", 0, 100, 0 },
+ { "darkgrey", 169, 169, 169 },
+ { "darkkhaki", 189, 183, 107 },
+ { "darkmagenta", 139, 0, 139 },
+ { "darkolivegreen", 85, 107, 47 },
+ { "darkorange", 255, 140, 0 },
+ { "darkorchid", 153, 50, 204 },
+ { "darkred", 139, 0, 0 },
+ { "darksalmon", 233, 150, 122 },
+ { "darkseagreen", 143, 188, 143 },
+ { "darkslateblue", 72, 61, 139 },
+ { "darkslategray", 47, 79, 79 },
+ { "darkslategrey", 47, 79, 79 },
+ { "darkturquoise", 0, 206, 209 },
+ { "darkviolet", 148, 0, 211 },
+ { "deeppink", 255, 20, 147 },
+ { "deepskyblue", 0, 191, 255 },
+ { "dimgray", 105, 105, 105 },
+ { "dimgrey", 105, 105, 105 },
+ { "dodgerblue", 30, 144, 255 },
+ { "firebrick", 178, 34, 34 },
+ { "floralwhite", 255, 250, 240 },
+ { "forestgreen", 34, 139, 34 },
+ { "fuchsia", 255, 0, 255 },
+ { "gainsboro", 220, 220, 220 },
+ { "ghostwhite", 248, 248, 255 },
+ { "gold", 255, 215, 0 },
+ { "goldenrod", 218, 165, 32 },
+ { "gray", 128, 128, 128 },
+ { "green", 0, 128, 0 },
+ { "greenyellow", 173, 255, 47 },
+ { "grey", 128, 128, 128 },
+ { "honeydew", 240, 255, 240 },
+ { "hotpink", 255, 105, 180 },
+ { "indianred", 205, 92, 92 },
+ { "indigo", 75, 0, 130 },
+ { "ivory", 255, 255, 240 },
+ { "khaki", 240, 230, 140 },
+ { "lavender", 230, 230, 250 },
+ { "lavenderblush", 255, 240, 245 },
+ { "lawngreen", 124, 252, 0 },
+ { "lemonchiffon", 255, 250, 205 },
+ { "lightblue", 173, 216, 230 },
+ { "lightcoral", 240, 128, 128 },
+ { "lightcyan", 224, 255, 255 },
+ { "lightgoldenrodyellow", 250, 250, 210 },
+ { "lightgray", 211, 211, 211 },
+ { "lightgreen", 144, 238, 144 },
+ { "lightgrey", 211, 211, 211 },
+ { "lightpink", 255, 182, 193 },
+ { "lightsalmon", 255, 160, 122 },
+ { "lightseagreen", 32, 178, 170 },
+ { "lightskyblue", 135, 206, 250 },
+ { "lightslategray", 119, 136, 153 },
+ { "lightslategrey", 119, 136, 153 },
+ { "lightsteelblue", 176, 196, 222 },
+ { "lightyellow", 255, 255, 224 },
+ { "lime", 0, 255, 0 },
+ { "limegreen", 50, 205, 50 },
+ { "linen", 250, 240, 230 },
+ { "magenta", 255, 0, 255 },
+ { "maroon", 128, 0, 0 },
+ { "mediumaquamarine", 102, 205, 170 },
+ { "mediumblue", 0, 0, 205 },
+ { "mediumorchid", 186, 85, 211 },
+ { "mediumpurple", 147, 112, 219 },
+ { "mediumseagreen", 60, 179, 113 },
+ { "mediumslateblue", 123, 104, 238 },
+ { "mediumspringgreen", 0, 250, 154 },
+ { "mediumturquoise", 72, 209, 204 },
+ { "mediumvioletred", 199, 21, 133 },
+ { "midnightblue", 25, 25, 112 },
+ { "mintcream", 245, 255, 250 },
+ { "mistyrose", 255, 228, 225 },
+ { "moccasin", 255, 228, 181 },
+ { "navajowhite", 255, 222, 173 },
+ { "navy", 0, 0, 128 },
+ { "oldlace", 253, 245, 230 },
+ { "olive", 128, 128, 0 },
+ { "olivedrab", 107, 142, 35 },
+ { "orange", 255, 165, 0 },
+ { "orangered", 255, 69, 0 },
+ { "orchid", 218, 112, 214 },
+ { "palegoldenrod", 238, 232, 170 },
+ { "palegreen", 152, 251, 152 },
+ { "paleturquoise", 175, 238, 238 },
+ { "palevioletred", 219, 112, 147 },
+ { "papayawhip", 255, 239, 213 },
+ { "peachpuff", 255, 218, 185 },
+ { "peru", 205, 133, 63 },
+ { "pink", 255, 192, 203 },
+ { "plum", 221, 160, 221 },
+ { "powderblue", 176, 224, 230 },
+ { "purple", 128, 0, 128 },
+ { "red", 255, 0, 0 },
+ { "rosybrown", 188, 143, 143 },
+ { "royalblue", 65, 105, 225 },
+ { "saddlebrown", 139, 69, 19 },
+ { "salmon", 250, 128, 114 },
+ { "sandybrown", 244, 164, 96 },
+ { "seagreen", 46, 139, 87 },
+ { "seashell", 255, 245, 238 },
+ { "sienna", 160, 82, 45 },
+ { "silver", 192, 192, 192 },
+ { "skyblue", 135, 206, 235 },
+ { "slateblue", 106, 90, 205 },
+ { "slategray", 112, 128, 144 },
+ { "slategrey", 112, 128, 144 },
+ { "snow", 255, 250, 250 },
+ { "springgreen", 0, 255, 127 },
+ { "steelblue", 70, 130, 180 },
+ { "tan", 210, 180, 140 },
+ { "teal", 0, 128, 128 },
+ { "thistle", 216, 191, 216 },
+ { "tomato", 255, 99, 71 },
+ { "turquoise", 64, 224, 208 },
+ { "violet", 238, 130, 238 },
+ { "wheat", 245, 222, 179 },
+ { "white", 255, 255, 255 },
+ { "whitesmoke", 245, 245, 245 },
+ { "yellow", 255, 255, 0 },
+ { "yellowgreen", 154, 205, 50 },
+};
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_PREDEFINED_H */
diff --git a/contrib/gstyle/gstyle-color-scale.c b/contrib/gstyle/gstyle-color-scale.c
new file mode 100644
index 0000000..7197b34
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-scale.c
@@ -0,0 +1,738 @@
+/* gstyle-color-scale.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-color-scale"
+
+#include <math.h>
+#include <cairo/cairo.h>
+#include <glib/gi18n.h>
+
+#include "gstyle-css-provider.h"
+#include "gstyle-utils.h"
+
+#include "gstyle-color-scale.h"
+
+static glong id_count = 1;
+
+typedef struct _ColorStop
+{
+ gint id;
+ gdouble offset;
+ GdkRGBA rgba;
+} ColorStop;
+
+struct _GstyleColorScale
+{
+ GtkScale parent_instance;
+
+ GstyleCssProvider *default_provider;
+
+ GstyleColorFilter filter;
+ gpointer filter_user_data;
+
+ GtkGesture *long_press_gesture;
+ GstyleColorScaleKind kind;
+ GSequence *custom_color_stops;
+ cairo_pattern_t *pattern;
+ cairo_pattern_t *checkered_pattern;
+
+ cairo_surface_t *data_surface;
+ guint32 *data_raw;
+ guint32 *data_raw_filtered;
+ gint data_stride;
+};
+
+G_DEFINE_TYPE (GstyleColorScale, gstyle_color_scale, GTK_TYPE_SCALE)
+
+enum {
+ PROP_0,
+ PROP_KIND,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+hold_action (GtkGestureLongPress *gesture,
+ gdouble x,
+ gdouble y,
+ GstyleColorScale *self)
+{
+ gboolean handled;
+
+ g_assert (GSTYLE_IS_COLOR_SCALE (self));
+
+ g_signal_emit_by_name (self, "popup-menu", &handled);
+}
+
+static void
+filter_data (GstyleColorScale *self)
+{
+ guint32 *src_data = self->data_raw;
+ guint32 *dst_data = self->data_raw_filtered;
+ GdkRGBA src_rgba;
+ GdkRGBA dst_rgba;
+
+ g_assert (GSTYLE_IS_COLOR_SCALE (self));
+ g_assert (self->filter != NULL);
+
+ for (gint i = 0; i < GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE; ++i)
+ {
+ unpack_rgba24 (src_data [i], &src_rgba);
+ self->filter (&src_rgba, &dst_rgba, self->filter_user_data);
+ dst_data [i] = pack_rgba24 (&dst_rgba);
+ }
+}
+
+/**
+ * gstyle_color_scale_get_filter: (skip):
+ * @self: A #GstyleColorScale
+ *
+ * Get a pointer to the current filter function or %NULL
+ * if no filter is actually set.
+ *
+ * Returns: (nullable): A GstyleColorFilter function pointer.
+ *
+ */
+GstyleColorFilter
+gstyle_color_scale_get_filter (GstyleColorScale *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR_SCALE (self), NULL);
+
+ return self->filter;
+}
+
+/* TODO: do a copy of orignal data so that we can remove or change the filter
+ * keeping the original datas */
+/**
+ * gstyle_color_scale_set_filter:
+ * @self: A #GstyleColorScale
+ * @filter_cb: (scope notified) (nullable): A GstyleColorFilter filter function or
+ * %NULL to unset the current filter. In this case, user_data is ignored.
+ * @user_data: (closure) (nullable): user data to pass when calling the filter function
+ *
+ * Set a filter to be used to change the drawing of the color scale
+ * when kind is set to custom-data (GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA)
+ * The data are filtered just after calling gstyle_color_scale_set_custom_data.
+ * So that if you remove or change the filter, you need to call it again.
+ *
+ */
+void
+gstyle_color_scale_set_filter (GstyleColorScale *self,
+ GstyleColorFilter filter_cb,
+ gpointer user_data)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_SCALE (self));
+
+ if (self->filter != filter_cb)
+ {
+ self->filter = filter_cb;
+ self->filter_user_data = (filter_cb == NULL) ? NULL : user_data;
+
+ if (self->kind != GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA)
+ return;
+
+ cairo_surface_flush (self->data_surface);
+ if (filter_cb == NULL)
+ memcpy (self->data_raw_filtered, self->data_raw, self->data_stride);
+ else
+ filter_data (self);
+
+ cairo_surface_mark_dirty (self->data_surface);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+/**
+ * gstyle_color_scale_clear_color_stops:
+ * @self: A #GstyleColorScale
+ *
+ * CLear all the color stops from the color scale.
+ *
+ */
+void
+gstyle_color_scale_clear_color_stops (GstyleColorScale *self)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_SCALE (self));
+
+ g_sequence_free (self->custom_color_stops);
+ self->custom_color_stops = g_sequence_new (NULL);
+}
+
+static gint
+compare_color_stop_by_offset (ColorStop *a,
+ ColorStop *b,
+ GstyleColorScale *self)
+{
+ gdouble delta;
+
+ g_assert (GSTYLE_IS_COLOR_SCALE (self));
+ g_assert (b != NULL && a != NULL);
+
+ delta = a->offset - b->offset;
+ if (delta < 0)
+ return -1;
+ else if (delta > 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * gstyle_color_scale_remove_color_stop:
+ * @self: A #GstyleColorScale
+ * @id: id of a color stop as returned by gstyle_color_scale_add_* functions
+ *
+ * Remove an existing color stop for the color scale.
+ *
+ * Returns: %TRUE if the color stop exist and is removed, %FALSE otherwise.
+ *
+ */
+gboolean
+gstyle_color_scale_remove_color_stop (GstyleColorScale *self,
+ gint id)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR_SCALE (self), FALSE);
+ g_return_val_if_fail (id <= 0, FALSE);
+
+ /* TODO: function code */
+ return TRUE;
+}
+
+/* TODO: use color stops with the data_raw instead of gradient pattern
+ * so that we can use filter on that too */
+
+/**
+ * gstyle_color_scale_add_rgba_color_stop:
+ * @self: A #GstyleColorScale
+ * @offset: position in the range [0, 1] of the color stop
+ * @rgba: A #GdkRGBA
+ *
+ * Set a color stop for the color scale.
+ * If there's no color stop at offset 0, a black opaque color stop is automatically added.
+ * If there's no color stop at offset 1, a white opaque color stop is automatically added.
+ *
+ * Returns: the id of the color stop or -1 if there's an existing
+ * stop color at the same offset
+ *
+ */
+gint
+gstyle_color_scale_add_rgba_color_stop (GstyleColorScale *self,
+ gdouble offset,
+ GdkRGBA *rgba)
+{
+ ColorStop *color_stop;
+ GSequenceIter *iter = NULL;
+
+ g_return_val_if_fail (GSTYLE_IS_COLOR_SCALE (self), -1);
+ g_return_val_if_fail (0.0 <= offset && offset <= 1.0, -1);
+ g_return_val_if_fail (rgba != NULL, -1);
+
+ color_stop = g_slice_new0 (ColorStop);
+ color_stop->id = id_count;
+ color_stop->offset = offset;
+ color_stop->rgba = *rgba;
+
+ if (!g_sequence_is_empty (self->custom_color_stops))
+ iter = g_sequence_lookup (self->custom_color_stops, color_stop,
+ (GCompareDataFunc)compare_color_stop_by_offset,
+ self);
+
+ if (iter != NULL)
+ {
+ g_slice_free (ColorStop, color_stop);
+ return -1;
+ }
+ else
+ {
+ g_sequence_insert_sorted (self->custom_color_stops, color_stop,
+ (GCompareDataFunc)compare_color_stop_by_offset,
+ self);
+ id_count += 1;
+
+ if (self->pattern != NULL)
+ cairo_pattern_destroy (self->pattern);
+
+ self->pattern = NULL;
+
+ if (gtk_widget_get_realized (GTK_WIDGET (self)))
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+
+ return color_stop->id;
+ }
+}
+
+/**
+ * gstyle_color_scale_set_custom_data:
+ * @self: A #GstyleColorScale
+ * @data: data location
+ *
+ * Set the data used to draw the color ramp if your have
+ * GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA kind selected.
+ *
+ * the location point to a memory array of 256 contiguous pixels
+ * using the CAIRO_FORMAT_RGB24 format (so 256 * 4 bytes)
+ * This result in using an guint32 for each pixel.
+ *
+ */
+void
+gstyle_color_scale_set_custom_data (GstyleColorScale *self,
+ guint32 *data)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_SCALE (self));
+ g_return_if_fail (data != NULL);
+
+ if (self->kind != GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA)
+ {
+ g_warning ("You need to set the kind to custom-data (GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA)"
+ " to use this function.");
+ return;
+ }
+
+ g_assert (self->data_surface != NULL);
+
+ cairo_surface_flush (self->data_surface);
+ memcpy (self->data_raw, data, self->data_stride);
+
+ if (self->filter == NULL)
+ memcpy (self->data_raw_filtered, self->data_raw, self->data_stride);
+ else
+ filter_data (self);
+
+ cairo_surface_mark_dirty (self->data_surface);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+/**
+ * gstyle_color_scale_add_color_stop:
+ * @self: A #GstyleColorScale
+ * @offset: position in the range [0, 1] of the color stop
+ * @red: red component of the color stop
+ * @green: red component of the color stop
+ * @blue: red component of the color stop
+ * @alpha: red component of the color stop
+ *
+ * Set a color stop for the #GstyleColorScale.
+ * If there's no color stop at offset 0, a black opaque color stop is automatically added.
+ * If there's no color stop at offset 1, a white opaque color stop is automatically added.
+ *
+ * Returns: the id of the color stop or -1 if there's an existing
+ * stop color at the same offset
+ *
+ */
+gint
+gstyle_color_scale_add_color_stop (GstyleColorScale *self,
+ gdouble offset,
+ gdouble red,
+ gdouble green,
+ gdouble blue,
+ gdouble alpha)
+{
+ GdkRGBA rgba = {red, green, blue, alpha};
+
+ g_return_val_if_fail (GSTYLE_IS_COLOR_SCALE (self), -1);
+
+ return gstyle_color_scale_add_rgba_color_stop (self, offset, &rgba);
+}
+
+/**
+ * gstyle_color_scale_get_kind:
+ * @self: A #GstyleColorScale
+ *
+ * Get the kind of gradient displayed in the scale.
+ *
+ * Returns: a #GstyleColorKind.
+ *
+ */
+GstyleColorScaleKind
+gstyle_color_scale_get_kind (GstyleColorScale *self)
+{
+ g_assert (GSTYLE_IS_COLOR_SCALE (self));
+
+ return self->kind;
+}
+
+/**
+ * gstyle_color_scale_set_kind:
+ * @self: A #GstyleColorScale
+ * @kind: A #GstyleColorKind
+ *
+ * Set the kind of gradient displayed in the scale.
+ * If you set the kind to GSTYLE_COLOR_SCALE_KIND_CUSTOM_STOPS,
+ * all the previously added color stops are cleared.
+ *
+ * if you set the kind to GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA,
+ * the previous data are freed.
+ *
+ */
+void
+gstyle_color_scale_set_kind (GstyleColorScale *self,
+ GstyleColorScaleKind kind)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_SCALE (self));
+
+ if (self->kind != kind)
+ {
+ self->kind = kind;
+ if (kind == GSTYLE_COLOR_SCALE_KIND_CUSTOM_STOPS)
+ gstyle_color_scale_clear_color_stops (self);
+ else if (kind == GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA)
+ {
+ self->data_stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24,
+ GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE);
+ self->data_raw = g_malloc0 (self->data_stride);
+ self->data_raw_filtered = g_malloc0 (self->data_stride);
+ self->data_surface = cairo_image_surface_create_for_data ((guchar *)self->data_raw_filtered,
+ CAIRO_FORMAT_RGB24,
+
GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE, 1,
+ self->data_stride);
+ }
+
+ if (self->pattern != NULL)
+ {
+ cairo_pattern_destroy (self->pattern);
+ self->pattern = NULL;
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_KIND]);
+
+ if (gtk_widget_get_realized (GTK_WIDGET (self)))
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+update_pattern (GstyleColorScale *self)
+{
+ cairo_pattern_t *pattern;
+
+ g_assert (GSTYLE_IS_COLOR_SCALE (self));
+
+ if (self->kind == GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA)
+ return;
+
+ pattern = cairo_pattern_create_linear (0, 0, 1, 0);
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_NONE);
+
+ if (self->kind != GSTYLE_COLOR_SCALE_KIND_CUSTOM_STOPS)
+ {
+ switch (self->kind)
+ {
+ case GSTYLE_COLOR_SCALE_KIND_HUE:
+ cairo_pattern_add_color_stop_rgba (pattern, 0.0000, 1.0, 0.0, 0.0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 0.1666, 1.0, 1.0, 0.0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 0.3333, 0.0, 1.0, 0.0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 0.5000, 0.0, 1.0, 1.0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 0.6666, 0.0, 0.0, 1.0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 0.8333, 1.0, 0.0, 1.0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 1.0000, 1.0, 0.0, 0.0, 1.0);
+ break;
+
+ case GSTYLE_COLOR_SCALE_KIND_GREY:
+ cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 0.0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1.0, 1.0, 1.0, 1.0);
+ break;
+
+ case GSTYLE_COLOR_SCALE_KIND_ALPHA:
+ cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 0.0, 0.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 0.0, 0.0, 1.0);
+ break;
+
+ case GSTYLE_COLOR_SCALE_KIND_RED:
+ cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 0.0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1.0, 0.0, 0.0, 1.0);
+ break;
+
+ case GSTYLE_COLOR_SCALE_KIND_GREEN:
+ cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 0.0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 1.0, 0.0, 1.0);
+ break;
+
+ case GSTYLE_COLOR_SCALE_KIND_BLUE:
+ cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 0.0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 0.0, 1.0, 1.0);
+ break;
+
+ case GSTYLE_COLOR_SCALE_KIND_CUSTOM_STOPS:
+ case GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA:
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ else
+ {
+ GSequenceIter *iter;
+ GSequenceIter *iter_offset_zero = NULL;
+ GSequenceIter *iter_offset_one = NULL;
+ ColorStop local_color_stop;
+ gint len;
+
+ if (!g_sequence_is_empty (self->custom_color_stops))
+ {
+ local_color_stop.offset = 0.0;
+ iter_offset_zero = g_sequence_lookup (self->custom_color_stops, &local_color_stop,
+ (GCompareDataFunc)compare_color_stop_by_offset,
+ self);
+
+ local_color_stop.offset = 1.0;
+ iter_offset_one = g_sequence_lookup (self->custom_color_stops, &local_color_stop,
+ (GCompareDataFunc)compare_color_stop_by_offset,
+ self);
+ }
+
+ if (iter_offset_zero == NULL)
+ cairo_pattern_add_color_stop_rgba (pattern, 0, 0.0, 0.0, 0.0, 1.0);
+
+ if (iter_offset_one == NULL)
+ cairo_pattern_add_color_stop_rgba (pattern, 0, 0.0, 0.0, 0.0, 1.0);
+
+ len = g_sequence_get_length (self->custom_color_stops);
+ for (gint i = 0; i < len; ++i)
+ {
+ ColorStop *color_stop;
+ GdkRGBA rgba;
+
+ iter = g_sequence_get_iter_at_pos (self->custom_color_stops, i);
+ color_stop = g_sequence_get (iter);
+ rgba = color_stop->rgba;
+ cairo_pattern_add_color_stop_rgba (pattern, color_stop->offset,
+ rgba.red, rgba.green, rgba.blue, rgba.alpha);
+ }
+ }
+
+ cairo_pattern_destroy (self->pattern);
+ self->pattern = pattern;
+}
+
+static gboolean
+gstyle_color_scale_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ GstyleColorScale *self = (GstyleColorScale *)widget;
+ GtkAllocation alloc;
+ gboolean inverted;
+ GdkRectangle rect;
+ cairo_matrix_t matrix;
+ cairo_pattern_t *data_pattern;
+
+ g_assert (GSTYLE_IS_COLOR_SCALE (self));
+ g_assert (cr != NULL);
+
+ gtk_widget_get_allocation (widget, &alloc);
+ gtk_range_get_range_rect (GTK_RANGE (self), &rect);
+
+ cairo_save (cr);
+ cairo_rectangle (cr, rect.x, rect.y, rect.width, rect.height);
+ cairo_clip (cr);
+
+ cairo_set_source_rgb (cr, 0.20, 0.20, 0.20);
+ cairo_paint (cr);
+ cairo_set_source_rgb (cr, 0.80, 0.80, 0.80);
+
+ cairo_matrix_init_scale (&matrix, 0.1, 0.1);
+ cairo_matrix_translate (&matrix, -rect.x - 1, -rect.y - 1);
+ cairo_pattern_set_matrix (self->checkered_pattern, &matrix);
+ cairo_mask (cr, self->checkered_pattern);
+
+ cairo_translate (cr, rect.x, rect.y);
+ cairo_scale (cr, rect.width, rect.height);
+
+ if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_VERTICAL)
+ {
+ cairo_rotate (cr, -G_PI_2);
+ cairo_scale (cr, -1, 1);
+ }
+
+ inverted = gtk_range_get_inverted (GTK_RANGE (self));
+ if (inverted)
+ {
+ cairo_translate (cr, 1, 0);
+ cairo_scale (cr, -1, 1);
+ }
+
+ if (self->kind != GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA)
+ {
+ if (self->pattern == NULL)
+ update_pattern (self);
+
+ cairo_set_source (cr, self->pattern);
+ cairo_paint (cr);
+ }
+ else
+ {
+ cairo_set_source_surface (cr, self->data_surface, 0.0, 0.0);
+ data_pattern = cairo_get_source (cr);
+ cairo_pattern_set_extend (data_pattern, CAIRO_EXTEND_NONE);
+ cairo_pattern_set_filter (data_pattern, CAIRO_FILTER_NEAREST);
+ cairo_matrix_init_scale (&matrix, 256.0, 1.0);
+ cairo_pattern_set_matrix (data_pattern, &matrix);
+ cairo_paint (cr);
+ }
+
+ cairo_restore (cr);
+
+ GTK_WIDGET_CLASS (gstyle_color_scale_parent_class)->draw (widget, cr);
+ return GDK_EVENT_PROPAGATE;
+}
+
+GstyleColorScale *
+gstyle_color_scale_new (GtkAdjustment *adjustment)
+{
+ return g_object_new (GSTYLE_TYPE_COLOR_SCALE,
+ "adjustment", adjustment,
+ NULL);
+}
+
+static void
+gstyle_color_scale_finalize (GObject *object)
+{
+ GstyleColorScale *self = (GstyleColorScale *)object;
+
+ G_OBJECT_CLASS (gstyle_color_scale_parent_class)->finalize (object);
+
+ g_clear_object (&self->long_press_gesture);
+ g_clear_object (&self->default_provider);
+ g_sequence_free (self->custom_color_stops);
+
+ cairo_pattern_destroy (self->checkered_pattern);
+ if (self->pattern != NULL)
+ cairo_pattern_destroy (self->pattern);
+
+ if (self->data_surface != NULL)
+ cairo_surface_destroy (self->data_surface);
+
+ g_free (self->data_raw);
+ g_free (self->data_raw_filtered);
+}
+
+static void
+gstyle_color_scale_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleColorScale *self = GSTYLE_COLOR_SCALE (object);
+
+ switch (prop_id)
+ {
+ case PROP_KIND:
+ g_value_set_enum (value, gstyle_color_scale_get_kind (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_color_scale_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleColorScale *self = GSTYLE_COLOR_SCALE (object);
+
+ switch (prop_id)
+ {
+ case PROP_KIND:
+ gstyle_color_scale_set_kind (self, g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_color_scale_class_init (GstyleColorScaleClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gstyle_color_scale_finalize;
+ object_class->get_property = gstyle_color_scale_get_property;
+ object_class->set_property = gstyle_color_scale_set_property;
+ widget_class->draw = gstyle_color_scale_draw;
+
+ properties [PROP_KIND] =
+ g_param_spec_enum ("kind",
+ "Kind",
+ "The kind of gradient used",
+ GSTYLE_TYPE_COLOR_SCALE_KIND,
+ GSTYLE_COLOR_SCALE_KIND_HUE,
+ (G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_css_name (widget_class, "gstylecolorscale");
+}
+
+static void
+free_color_stop (ColorStop *color_stop)
+{
+ g_slice_free (ColorStop, color_stop);
+}
+
+static void
+gstyle_color_scale_init (GstyleColorScale *self)
+{
+ GtkStyleContext *context;
+
+ gtk_widget_add_events (GTK_WIDGET (self), GDK_TOUCH_MASK);
+
+ self->long_press_gesture = gtk_gesture_long_press_new (GTK_WIDGET (self));
+ g_signal_connect (self->long_press_gesture, "pressed", G_CALLBACK (hold_action), self);
+
+ gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->long_press_gesture),
+ GTK_PHASE_TARGET);
+
+ self->custom_color_stops = g_sequence_new ((GDestroyNotify)free_color_stop);
+ self->checkered_pattern = gstyle_utils_get_checkered_pattern ();
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ self->default_provider = gstyle_css_provider_init_default (gtk_style_context_get_screen (context));
+
+ gtk_scale_set_draw_value (GTK_SCALE (self), FALSE);
+}
+
+GType
+gstyle_color_scale_kind_get_type (void)
+{
+ static GType type_id;
+ static const GEnumValue values[] = {
+ { GSTYLE_COLOR_SCALE_KIND_HUE, "GSTYLE_COLOR_SCALE_KIND_HUE", "hue" },
+ { GSTYLE_COLOR_SCALE_KIND_GREY, "GSTYLE_COLOR_SCALE_KIND_GREY", "grey" },
+ { GSTYLE_COLOR_SCALE_KIND_ALPHA, "GSTYLE_COLOR_SCALE_KIND_ALPHA", "alpha" },
+ { GSTYLE_COLOR_SCALE_KIND_RED, "GSTYLE_COLOR_SCALE_KIND_RED", "red" },
+ { GSTYLE_COLOR_SCALE_KIND_GREEN, "GSTYLE_COLOR_SCALE_KIND_GREEN", "green" },
+ { GSTYLE_COLOR_SCALE_KIND_BLUE, "GSTYLE_COLOR_SCALE_KIND_BLUE", "blue" },
+ { GSTYLE_COLOR_SCALE_KIND_CUSTOM_STOPS, "GSTYLE_COLOR_SCALE_KIND_CUSTOM", "custom-stops" },
+ { GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA, "GSTYLE_COLOR_SCALE_KIND_CUSTOM", "custom-data" },
+ { 0 }
+ };
+
+ if (g_once_init_enter (&type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_enum_register_static ("GstyleColorScaleKind", values);
+ g_once_init_leave (&type_id, _type_id);
+ }
+
+ return type_id;
+}
diff --git a/contrib/gstyle/gstyle-color-scale.h b/contrib/gstyle/gstyle-color-scale.h
new file mode 100644
index 0000000..67825c3
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-scale.h
@@ -0,0 +1,78 @@
+/* gstyle-color-scale.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_SCALE_H
+#define GSTYLE_COLOR_SCALE_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "gstyle-color-filter.h"
+
+G_BEGIN_DECLS
+
+#define GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE (256)
+#define GSTYLE_COLOR_SCALE_CUSTOM_DATA_BYTE_SIZE (GSTYLE_COLOR_SCALE_CUSTOM_DATA_PIXEL_SIZE * 4)
+
+typedef enum
+{
+ GSTYLE_COLOR_SCALE_KIND_HUE,
+ GSTYLE_COLOR_SCALE_KIND_GREY,
+ GSTYLE_COLOR_SCALE_KIND_ALPHA,
+ GSTYLE_COLOR_SCALE_KIND_RED,
+ GSTYLE_COLOR_SCALE_KIND_GREEN,
+ GSTYLE_COLOR_SCALE_KIND_BLUE,
+ GSTYLE_COLOR_SCALE_KIND_CUSTOM_STOPS,
+ GSTYLE_COLOR_SCALE_KIND_CUSTOM_DATA
+} GstyleColorScaleKind;
+
+#define GSTYLE_TYPE_COLOR_SCALE_KIND (gstyle_color_scale_kind_get_type())
+#define GSTYLE_TYPE_COLOR_SCALE (gstyle_color_scale_get_type())
+
+G_DECLARE_FINAL_TYPE (GstyleColorScale, gstyle_color_scale, GSTYLE, COLOR_SCALE, GtkScale)
+
+GType gstyle_color_scale_kind_get_type (void);
+
+GstyleColorScale *gstyle_color_scale_new (GtkAdjustment *adjustment);
+gint gstyle_color_scale_add_rgba_color_stop (GstyleColorScale *self,
+ gdouble offset,
+ GdkRGBA *rgba);
+gint gstyle_color_scale_add_color_stop (GstyleColorScale *self,
+ gdouble offset,
+ gdouble red,
+ gdouble green,
+ gdouble blue,
+ gdouble alpha);
+void gstyle_color_scale_clear_color_stops (GstyleColorScale *self);
+GstyleColorFilter gstyle_color_scale_get_filter (GstyleColorScale *self);
+GstyleColorScaleKind gstyle_color_scale_get_kind (GstyleColorScale *self);
+
+gboolean gstyle_color_scale_remove_color_stop (GstyleColorScale *self,
+ gint id);
+void gstyle_color_scale_set_custom_data (GstyleColorScale *self,
+ guint32 *data);
+void gstyle_color_scale_set_filter (GstyleColorScale *self,
+ GstyleColorFilter filter_cb,
+ gpointer user_data);
+void gstyle_color_scale_set_kind (GstyleColorScale *self,
+ GstyleColorScaleKind kind);
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_SCALE_H */
diff --git a/contrib/gstyle/gstyle-color-widget.c b/contrib/gstyle/gstyle-color-widget.c
new file mode 100644
index 0000000..1cca1a8
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-widget.c
@@ -0,0 +1,1252 @@
+/* gstyle-color-widget.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-color-widget"
+
+#include <cairo.h>
+#include <string.h>
+#include <gdk/gdk.h>
+
+#include "gstyle-css-provider.h"
+#include "gstyle-palette-widget.h"
+#include "gstyle-private.h"
+
+#include "gstyle-color-widget.h"
+
+struct _GstyleColorWidget
+{
+ GtkBin parent_instance;
+
+ GstyleCssProvider *default_provider;
+
+ GtkLabel *label;
+ GstyleColor *color;
+ GstyleColorKind fallback_name_kind;
+
+ GtkBorder cached_margin;
+ GtkBorder cached_border;
+
+ cairo_pattern_t *checkered_pattern;
+
+ GtkTargetList *target_list;
+ GstyleColorWidget *dnd_color_widget;
+ GtkWidget *dnd_window;
+ gboolean is_on_drag;
+
+ GtkGesture *drag_gesture;
+
+ GstylePaletteWidgetViewMode container_view_mode;
+ GstyleColorWidgetDndLockFlags dnd_lock : 4;
+ guint is_in_palette_widget : 1;
+ guint is_name_visible : 1;
+ guint is_fallback_name_visible : 1;
+};
+
+G_DEFINE_TYPE (GstyleColorWidget, gstyle_color_widget, GTK_TYPE_BIN)
+
+#define GSTYLE_COLOR_WIDGET_DROP_BORDER_PERCENT 0.20
+#define GSTYLE_COLOR_WIDGET_DRAG_ICON_OPACITY 0.8
+
+enum {
+ PROP_0,
+ PROP_COLOR,
+ PROP_DND_LOCK,
+ PROP_NAME_VISIBLE,
+ PROP_FALLBACK_NAME_KIND,
+ PROP_FALLBACK_NAME_VISIBLE,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+is_in_drop_zone (GstyleColorWidget *self,
+ gint x,
+ gint y)
+{
+ GtkAllocation alloc;
+ gint start_limit;
+ gint stop_limit;
+ gint dest_ref;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+ if (self->is_in_palette_widget)
+ {
+ if (self->container_view_mode == GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST)
+ {
+ start_limit = alloc.height * GSTYLE_COLOR_WIDGET_DROP_BORDER_PERCENT;
+ stop_limit = alloc.height * (1.0 - GSTYLE_COLOR_WIDGET_DROP_BORDER_PERCENT);
+ dest_ref = y;
+ }
+ else
+ {
+ start_limit = alloc.width * GSTYLE_COLOR_WIDGET_DROP_BORDER_PERCENT;
+ stop_limit = alloc.width * (1.0 - GSTYLE_COLOR_WIDGET_DROP_BORDER_PERCENT);
+ dest_ref = x;
+ }
+ }
+ else
+ {
+ /* dest_ref doesn't matter here, we just need to allow for the whole widget size */
+ start_limit = 0;
+ stop_limit = alloc.width;
+ dest_ref = x;
+ }
+
+ return (start_limit < dest_ref && dest_ref < stop_limit);
+}
+
+static gboolean
+gstyle_color_widget_on_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+ GdkAtom target;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ target = gtk_drag_dest_find_target (widget, context, NULL);
+
+ if ((target == gdk_atom_intern_static_string ("GSTYLE_COLOR_WIDGET") ||
+ target == gdk_atom_intern_static_string ("application/x-color") ||
+ gtk_targets_include_text (&target, 1)) &&
+ is_in_drop_zone (self, x, y))
+ {
+ gtk_drag_highlight (widget);
+ gdk_drag_status (context, GDK_ACTION_COPY, time);
+ return TRUE;
+ }
+
+ gdk_drag_status (context, 0, time);
+ return FALSE;
+}
+
+static void
+gstyle_color_widget_on_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ gtk_drag_unhighlight (widget);
+}
+
+static gboolean
+gstyle_color_widget_on_drag_drop (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+ GdkAtom target;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ target = gtk_drag_dest_find_target (widget, context, NULL);
+ if ((target == gdk_atom_intern_static_string ("GSTYLE_COLOR_WIDGET") ||
+ target == gdk_atom_intern_static_string ("application/x-color") ||
+ gtk_targets_include_text (&target, 1)) &&
+ is_in_drop_zone (self, x, y))
+ {
+ gtk_drag_get_data (widget, context, target, time);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+dnd_color_fill (GstyleColorWidget *self,
+ GstyleColor *src_color,
+ GstyleColor *dst_color)
+{
+ const gchar *name;
+ GdkRGBA src_rgba;
+ GdkRGBA dst_rgba;
+
+ g_assert (GSTYLE_COLOR_WIDGET (self));
+ g_assert (GSTYLE_COLOR (src_color));
+ g_assert (GSTYLE_COLOR (dst_color));
+
+ gstyle_color_fill_rgba (src_color, &src_rgba);
+ gstyle_color_fill_rgba (dst_color, &dst_rgba);
+ if (!(self->dnd_lock & GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_COLOR))
+ {
+ dst_rgba.red = src_rgba.red;
+ dst_rgba.green = src_rgba.green;
+ dst_rgba.blue = src_rgba.blue;
+ }
+
+ if (!(self->dnd_lock & GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_ALPHA))
+ dst_rgba.alpha = src_rgba.alpha;
+
+ gstyle_color_set_rgba (self->color, &dst_rgba);
+
+ if (!(self->dnd_lock & GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_KIND))
+ gstyle_color_set_kind (dst_color, gstyle_color_get_kind (src_color));
+
+ if (!(self->dnd_lock & GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_NAME))
+ {
+ name = gstyle_color_get_name (src_color);
+ gstyle_color_set_name (dst_color, name);
+ }
+}
+
+static void
+gstyle_color_widget_on_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *data,
+ guint info,
+ guint time)
+{
+ GstyleColorWidget *self = GSTYLE_COLOR_WIDGET (widget);
+ GstyleColor * const *src_color;
+ g_autofree gchar *color_string = NULL;
+ GstyleColorKind kind;
+ GdkAtom target;
+ guint16 *data_rgba;
+ GdkRGBA rgba;
+ gint len;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ target = gtk_selection_data_get_target (data);
+ if (target == gdk_atom_intern_static_string ("GSTYLE_COLOR_WIDGET"))
+ {
+ /* TODO: check if the color widget is coming from a PaletteWidget container */
+ src_color = (void*)gtk_selection_data_get_data (data);
+ if (*src_color != self->color)
+ dnd_color_fill (self, *src_color, self->color);
+
+ gtk_drag_finish (context, TRUE, FALSE, time);
+
+ return;
+ }
+ else if (target == gdk_atom_intern_static_string ("application/x-color"))
+ {
+ len = gtk_selection_data_get_length (data);
+ if (len < 0 )
+ goto failed;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+ data_rgba = (guint16 *)gtk_selection_data_get_data (data);
+
+#pragma GCC diagnostic pop
+
+ rgba.red = data_rgba[0] / 65535.;
+ rgba.green = data_rgba[1] / 65535.;
+ rgba.blue = data_rgba[2] / 65535.;
+ rgba.alpha = data_rgba[3] / 65535.;
+
+ gstyle_color_set_rgba (self->color, &rgba);
+ gtk_drag_finish (context, TRUE, FALSE, time);
+
+ return;
+ }
+ else if (gtk_targets_include_text (&target, 1))
+ {
+ color_string = (gchar *)gtk_selection_data_get_text (data);
+ if (!gstyle_str_empty0 (color_string))
+ {
+ if (!gstyle_color_parse_color_string (color_string, &rgba, &kind))
+ goto failed;
+
+ gstyle_color_set_rgba (self->color, &rgba);
+ gtk_drag_finish (context, TRUE, FALSE, time);
+ }
+ }
+
+failed:
+ gtk_drag_finish (context, FALSE, FALSE, time);
+}
+
+static void
+gstyle_color_widget_on_drag_begin (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+ GtkAllocation allocation;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ gtk_widget_get_allocation (GTK_WIDGET (widget), &allocation);
+ self->dnd_color_widget = gstyle_color_widget_copy (GSTYLE_COLOR_WIDGET (widget));
+ self->dnd_window = gtk_window_new (GTK_WINDOW_POPUP);
+
+ gtk_widget_set_size_request (self->dnd_window, allocation.width, allocation.height);
+ gtk_window_set_screen (GTK_WINDOW (self->dnd_window), gtk_widget_get_screen (widget));
+
+ gtk_container_add (GTK_CONTAINER (self->dnd_window), GTK_WIDGET (self->dnd_color_widget));
+ gtk_widget_show_all (self->dnd_window);
+ gtk_widget_set_opacity (self->dnd_window, GSTYLE_COLOR_WIDGET_DRAG_ICON_OPACITY);
+
+ gtk_drag_set_icon_widget (context, self->dnd_window, 0, 0);
+}
+
+/* The multi-press gesture used by the flowbox to select a child
+ * forbid us to use dnd so we need to catch it here and select yourself
+ * the child
+ */
+static void
+gstyle_color_widget_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
+ guint n_press,
+ gdouble x,
+ gdouble y,
+ GstyleColorWidget *self)
+{
+ GtkWidget *container;
+ GtkWidget *child;
+
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+
+ child = gtk_widget_get_parent (GTK_WIDGET (self));
+ if (child != NULL)
+ {
+ if (GTK_IS_LIST_BOX_ROW (child))
+ {
+ container = gtk_widget_get_parent (GTK_WIDGET (child));
+ if (container != NULL && GTK_IS_LIST_BOX (container))
+ {
+ gtk_list_box_select_row (GTK_LIST_BOX (container), GTK_LIST_BOX_ROW (child));
+ if (n_press == 2)
+ g_signal_emit_by_name (container, "row-activated", child);
+ }
+ }
+ else if (GTK_IS_FLOW_BOX_CHILD (child))
+ {
+ container = gtk_widget_get_parent (GTK_WIDGET (child));
+ if (container != NULL && GTK_IS_FLOW_BOX (container))
+ {
+ gtk_flow_box_select_child (GTK_FLOW_BOX (container), GTK_FLOW_BOX_CHILD (child));
+ if (n_press == 2)
+ g_signal_emit_by_name (container, "child-activated", child);
+ }
+ }
+ }
+}
+
+static void
+gstyle_color_widget_on_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *data,
+ guint info,
+ guint time)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+ GdkAtom target = gtk_selection_data_get_target (data);
+ guint16 data_rgba[4];
+ GdkRGBA rgba;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ if (target == gdk_atom_intern_static_string ("GSTYLE_COLOR_WIDGET"))
+ gtk_selection_data_set (data, target, 8, (void*)&self->color, sizeof (gpointer));
+ else if (target == gdk_atom_intern_static_string ("application/x-color"))
+ {
+ gstyle_color_fill_rgba (self->color, &rgba);
+ data_rgba[0] = (guint16) (rgba.red * 65535);
+ data_rgba[1] = (guint16) (rgba.green * 65535);
+ data_rgba[2] = (guint16) (rgba.blue * 65535);
+ data_rgba[3] = (guint16) (rgba.alpha * 65535);
+
+ gtk_selection_data_set (data, target, 16, (void*)&data_rgba, 8);
+ }
+ else if (gtk_targets_include_text (&target, 1))
+ {
+ g_autofree gchar *name = NULL;
+
+ name = gstyle_color_to_string (self->color, GSTYLE_COLOR_KIND_ORIGINAL);
+ if (name == NULL)
+ name = gstyle_color_to_string (self->color, GSTYLE_COLOR_KIND_RGB_HEX6);
+
+ gtk_selection_data_set_text (data, name, -1);
+ }
+}
+
+static void
+gstyle_color_widget_on_drag_end (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ gtk_widget_destroy (self->dnd_window);
+ self->dnd_window = NULL;
+ self->dnd_color_widget = NULL;
+ self->is_on_drag = FALSE;
+}
+
+static gboolean
+gstyle_color_widget_on_drag_failed (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkDragResult result)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ return FALSE;
+}
+
+static void
+update_border_and_margin (GstyleColorWidget *self)
+{
+ GtkStyleContext *style_context;
+ GtkStateFlags state;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ state = gtk_style_context_get_state (style_context);
+
+ gtk_style_context_get_margin (style_context, state, &self->cached_margin);
+ gtk_style_context_get_border (style_context, state, &self->cached_border);
+}
+
+static void
+gstyle_color_widget_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+ GtkAllocation child_allocation;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+
+ gtk_widget_set_allocation (widget, allocation);
+
+ if (self->label && gtk_widget_get_visible (GTK_WIDGET (self->label)))
+ {
+ child_allocation.x = 0;
+ child_allocation.y = 0;
+ child_allocation.width = allocation->width;
+ child_allocation.height = allocation->height;
+
+ gtk_widget_size_allocate (GTK_WIDGET (self->label), &child_allocation);
+ }
+
+ if (gtk_widget_get_realized (widget))
+ gdk_window_move_resize (gtk_widget_get_window (widget),
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
+}
+
+static void
+gstyle_color_widget_get_preferred_width (GtkWidget *widget,
+ gint *min_width,
+ gint *nat_width)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+ GtkWidget *child;
+ gint spacing;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+
+ *min_width = 1;
+ *nat_width = 1;
+
+ update_border_and_margin (self);
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child && gtk_widget_get_visible (child))
+ gtk_widget_get_preferred_width (child, min_width, nat_width);
+
+ spacing = self->cached_border.left +
+ self->cached_border.right +
+ self->cached_margin.left +
+ self->cached_margin.right;
+
+ *min_width += spacing;
+ *nat_width += spacing;
+}
+
+static void
+gstyle_color_widget_get_preferred_height (GtkWidget *widget,
+ gint *min_height,
+ gint *nat_height)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+ GtkWidget *child;
+ gint spacing;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+
+ *min_height = 1;
+ *nat_height = 1;
+
+ update_border_and_margin (self);
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child && gtk_widget_get_visible (child))
+ gtk_widget_get_preferred_height (child, min_height, nat_height);
+
+ spacing = self->cached_border.top +
+ self->cached_border.bottom +
+ self->cached_margin.top +
+ self->cached_margin.bottom;
+
+ *min_height += spacing;
+ *nat_height += spacing;
+}
+
+static gboolean
+gstyle_color_widget_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+ GtkStyleContext *style_context;
+ GdkRectangle margin_box;
+ GdkRectangle border_box;
+ cairo_matrix_t matrix;
+ GdkRGBA bg_color = {0};
+ gint radius;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (cr != NULL);
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ gtk_widget_get_allocation (widget, &margin_box);
+ margin_box.x = margin_box.y = 0;
+
+ gstyle_utils_get_rect_resized_box (margin_box, &margin_box, &self->cached_margin);
+ gstyle_utils_get_rect_resized_box (margin_box, &border_box, &self->cached_border);
+ cairo_save (cr);
+
+ if (self->color != NULL)
+ {
+ gtk_style_context_get (style_context,
+ gtk_style_context_get_state (style_context),
+ "border-radius", &radius,
+ NULL);
+
+ gstyle_color_fill_rgba (self->color, &bg_color);
+
+ cairo_new_path (cr);
+ draw_cairo_round_box (cr, border_box, radius, radius, radius, radius);
+ }
+ else
+ cairo_rectangle (cr, border_box.x, border_box.y, border_box.width, border_box.height);
+
+ cairo_clip_preserve (cr);
+
+ cairo_set_source_rgb (cr, 0.20, 0.20, 0.20);
+ cairo_paint (cr);
+ cairo_set_source_rgb (cr, 0.80, 0.80, 0.80);
+
+ cairo_matrix_init_scale (&matrix, 0.1, 0.1);
+ cairo_matrix_translate (&matrix, -border_box.x, -border_box.y);
+ cairo_pattern_set_matrix (self->checkered_pattern, &matrix);
+ cairo_mask (cr, self->checkered_pattern);
+
+ if (self->color != NULL)
+ {
+ gdk_cairo_set_source_rgba (cr, &bg_color);
+ cairo_fill (cr);
+ }
+ else
+ gtk_render_background (style_context, cr, border_box.x, border_box.y, border_box.width,
border_box.height);
+
+ cairo_restore (cr);
+ gtk_render_frame (gtk_widget_get_style_context (widget), cr,
+ margin_box.x, margin_box.y, margin_box.width, margin_box.height);
+
+ return GTK_WIDGET_CLASS (gstyle_color_widget_parent_class)->draw (widget, cr);
+}
+
+static void
+update_label_visibility (GstyleColorWidget *self)
+{
+ const gchar *color_name;
+ g_autofree gchar *fallback_name = NULL;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+
+ if (self->color == NULL)
+ {
+ if (gtk_widget_is_visible (GTK_WIDGET (self->label)))
+ gtk_widget_hide (GTK_WIDGET (self->label));
+
+ return;
+ }
+
+ if (self->is_name_visible)
+ {
+ color_name = gstyle_color_get_name (self->color);
+ if (color_name != NULL)
+ {
+ gtk_label_set_text (self->label, color_name);
+ if (!gtk_widget_is_visible (GTK_WIDGET (self->label)))
+ gtk_widget_show (GTK_WIDGET (self->label));
+
+ return;
+ }
+ }
+
+ if (self->is_fallback_name_visible)
+ {
+ fallback_name = gstyle_color_to_string (self->color, self->fallback_name_kind);
+ gtk_label_set_text (self->label, fallback_name);
+ if (!gtk_widget_is_visible (GTK_WIDGET (self->label)))
+ gtk_widget_show (GTK_WIDGET (self->label));
+ }
+ else
+ gtk_widget_hide (GTK_WIDGET (self->label));
+}
+
+static void
+match_label_color (GstyleColorWidget *self,
+ GstyleColor *color)
+{
+ PangoLayout *layout;
+ PangoAttrList *attr_list;
+ PangoAttribute *attr;
+ GdkRGBA rgba;
+ GdkRGBA dst_rgba;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (GSTYLE_IS_COLOR (color));
+
+ layout = gtk_label_get_layout (self->label);
+ attr_list = pango_layout_get_attributes (layout);
+ if (attr_list == NULL)
+ {
+ attr_list = pango_attr_list_new ();
+ gtk_label_set_attributes (self->label, attr_list);
+ pango_attr_list_unref (attr_list);
+ }
+
+ gstyle_color_fill_rgba (color, &rgba);
+ gstyle_utils_get_contrasted_rgba (rgba, &dst_rgba);
+ attr = pango_attr_foreground_new (dst_rgba.red * 0xffff, dst_rgba.green * 0xffff, dst_rgba.blue * 0xffff);
+ pango_attr_list_change (attr_list, attr);
+ attr = pango_attr_background_new (rgba.red * 0xffff, rgba.green * 0xffff, rgba.blue * 0xffff);
+ pango_attr_list_change (attr_list, attr);
+}
+
+static void
+gstyle_color_widget_rgba_notify_cb (GstyleColorWidget *self,
+ GParamSpec *pspec,
+ GstyleColor *color)
+{
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (G_IS_PARAM_SPEC (pspec));
+ g_assert (GSTYLE_IS_COLOR (color));
+
+ update_label_visibility (self);
+ match_label_color (self, color);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+gstyle_color_widget_name_notify_cb (GstyleColorWidget *self,
+ GParamSpec *pspec,
+ GstyleColor *color)
+{
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (G_IS_PARAM_SPEC (pspec));
+ g_assert (GSTYLE_IS_COLOR (color));
+
+ update_label_visibility (self);
+}
+
+static void
+gstyle_color_widget_disconnect_color (GstyleColorWidget *self)
+{
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+ g_assert (GSTYLE_IS_COLOR (self->color));
+
+ g_signal_handlers_disconnect_by_func (self->color,
+ G_CALLBACK (gstyle_color_widget_rgba_notify_cb),
+ self);
+
+ g_signal_handlers_disconnect_by_func (self->color,
+ G_CALLBACK (gstyle_color_widget_name_notify_cb),
+ self);
+}
+
+/**
+ * gstyle_color_widget_copy:
+ * @self: A #GstyleColorWidget
+ *
+ * Copy the given ##GstyleColorWidget.
+ * Notice that the underlaying #GstyleColor is shared
+ * between the two widgets (the copy increase the ref count)
+ *
+ * Returns: (transfer full): a new #GstyleColorWidget.
+ *
+ */
+GstyleColorWidget *
+gstyle_color_widget_copy (GstyleColorWidget *self)
+{
+ GstyleColorWidget *color_widget;
+ GstyleColor *color;
+ gboolean name_visible;
+ GstyleColorKind fallback_name_kind;
+ gboolean fallback_name_visible;
+
+ g_return_val_if_fail (GSTYLE_IS_COLOR_WIDGET (self), NULL);
+
+ color = gstyle_color_widget_get_color (self);
+ name_visible = gstyle_color_widget_get_name_visible (self);
+ fallback_name_visible = gstyle_color_widget_get_name_visible (self);
+ fallback_name_kind = gstyle_color_widget_get_fallback_name_kind (self);
+
+ color_widget = gstyle_color_widget_new_with_color (color);
+ gstyle_color_widget_set_name_visible (color_widget, name_visible);
+ gstyle_color_widget_set_name_visible (color_widget, fallback_name_visible);
+ gstyle_color_widget_set_fallback_name_kind (color_widget, fallback_name_kind);
+
+ return color_widget;
+}
+
+/**
+ * gstyle_color_widget_set_color:
+ * @self: A #GstyleColorWidget
+ * @color: (nullable): A #GstyleColor or %NULL
+ *
+ * Set the #GstyleColor for the #GstyleColorWidget.
+ *
+ */
+void
+gstyle_color_widget_set_color (GstyleColorWidget *self,
+ GstyleColor *color)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_WIDGET (self));
+ g_return_if_fail (GSTYLE_IS_COLOR (color) || color == NULL);
+
+ if (self->color != color)
+ {
+ if (self->color != NULL)
+ {
+ gstyle_color_widget_disconnect_color (self);
+ g_clear_object (&self->color);
+ }
+
+ if (color != NULL)
+ {
+ self->color = g_object_ref (color);
+
+ g_signal_connect_object (self->color,
+ "notify::rgba",
+ G_CALLBACK (gstyle_color_widget_rgba_notify_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->color,
+ "notify::name",
+ G_CALLBACK (gstyle_color_widget_name_notify_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ match_label_color (self, color);
+ }
+
+ update_label_visibility (self);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR]);
+ }
+}
+
+/**
+ * gstyle_color_widget_get_name_visible:
+ * @self: a #GstyleColorWidget
+ *
+ * Get the visibily of the #GstyleColorWidget name.
+ *
+ * Returns: %TRUE if the name should be visible, %FALSE otherwise.
+ *
+ */
+gboolean
+gstyle_color_widget_get_name_visible (GstyleColorWidget *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR_WIDGET (self), FALSE);
+
+ return self->is_name_visible;
+}
+
+/**
+ * gstyle_color_widget_get_fallback_name_visible:
+ * @self: a #GstyleColorWidget
+ *
+ * Get the visibily of the #GstyleColorWidget fallback name.
+ *
+ * Returns: %TRUE if the name should be visible, %FALSE otherwise.
+ *
+ */
+gboolean
+gstyle_color_widget_get_fallback_name_visible (GstyleColorWidget *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR_WIDGET (self), FALSE);
+
+ return self->is_fallback_name_visible;
+}
+
+/**
+ * gstyle_color_widget_get_fallback_name_kind:
+ * @self: a #GstyleColorWidget
+ *
+ * Get the kind of the #GstyleColorWidget fallback name.
+ *
+ * Returns: the #GstyleColorKind used for the fallback name.
+ *
+ */
+GstyleColorKind
+gstyle_color_widget_get_fallback_name_kind (GstyleColorWidget *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR_WIDGET (self), GSTYLE_COLOR_KIND_UNKNOW);
+
+ return self->fallback_name_kind;
+}
+
+/**
+ * gstyle_color_widget_set_name_visible:
+ * @self: a #GstyleColorWidget
+ * @visible: name visibility
+ *
+ * Set the visibily of the #GstyleColorWidget name.
+ *
+ */
+void
+gstyle_color_widget_set_name_visible (GstyleColorWidget *self,
+ gboolean visible)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_WIDGET (self));
+
+ self->is_name_visible = visible;
+ update_label_visibility (self);
+}
+
+/**
+ * gstyle_color_widget_set_fallback_name_visible:
+ * @self: a #GstyleColorWidget
+ * @visible: fallback name visibility
+ *
+ * Set the visibily of the #GstyleColorWidget fallback name.
+ *
+ */
+void
+gstyle_color_widget_set_fallback_name_visible (GstyleColorWidget *self,
+ gboolean visible)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_WIDGET (self));
+
+ self->is_fallback_name_visible = visible;
+ update_label_visibility (self);
+}
+
+/**
+ * gstyle_color_widget_set_fallback_name_kind:
+ * @self: a #GstyleColorWidget
+ * @kind: a #GstyleColorKind
+ *
+ * Set the kind of the #GstyleColorWidget fallback name.
+ *
+ */
+void
+gstyle_color_widget_set_fallback_name_kind (GstyleColorWidget *self,
+ GstyleColorKind kind)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR_WIDGET (self));
+
+ self->fallback_name_kind = kind;
+ update_label_visibility (self);
+}
+
+/**
+ * gstyle_color_widget_get_color:
+ * @self: a #GstyleColorWidget
+ *
+ * Get the #GstyleColor of the #GstyleColorWidget.
+ *
+ * Returns: (transfer none): The current affected #GstyleColor.
+ *
+ */
+GstyleColor *
+gstyle_color_widget_get_color (GstyleColorWidget *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR_WIDGET (self), NULL);
+
+ return self->color;
+}
+
+/**
+ * gstyle_color_widget_new:
+ *
+ * Returns: A new #GstyleColorWidget.
+ *
+ */
+GstyleColorWidget *
+gstyle_color_widget_new (void)
+{
+ return g_object_new (GSTYLE_TYPE_COLOR_WIDGET, NULL);
+}
+
+/**
+ * gstyle_color_widget_new_with_color:
+ * @color: A #GstyleColor
+ *
+ * Returns: A new #GstyleColorWidget.with @color affected.
+ *
+ */
+GstyleColorWidget *
+gstyle_color_widget_new_with_color (GstyleColor *color)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR (color), NULL);
+
+ return g_object_new (GSTYLE_TYPE_COLOR_WIDGET,
+ "color", color,
+ NULL);
+}
+
+static void
+update_container_parent_informations (GstyleColorWidget *self)
+{
+ GtkWidget *parent;
+ GtkWidget *grand_parent;
+ GtkWidget *container;
+ GstylePaletteWidget *palette_widget;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+
+ parent = gtk_widget_get_parent (GTK_WIDGET (self));
+ if (GTK_IS_LIST_BOX_ROW (parent) || GTK_IS_FLOW_BOX_CHILD (parent))
+ {
+ grand_parent = gtk_widget_get_parent (GTK_WIDGET (parent));
+ if (grand_parent != NULL && g_str_has_prefix (gtk_widget_get_name (grand_parent), "palette"))
+ {
+ self->is_in_palette_widget = TRUE;
+ container = gtk_widget_get_ancestor (grand_parent, GSTYLE_TYPE_PALETTE_WIDGET);
+ if (container != NULL && GSTYLE_IS_PALETTE_WIDGET (container))
+ {
+ palette_widget = GSTYLE_PALETTE_WIDGET (container);
+ self->container_view_mode = gstyle_palette_widget_get_view_mode (GSTYLE_PALETTE_WIDGET
(palette_widget));
+
+ return;
+ }
+ }
+ }
+
+ self->is_in_palette_widget = FALSE;
+}
+
+static void
+gstyle_color_widget_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *previous_toplevel)
+{
+ GstyleColorWidget *self = (GstyleColorWidget *)widget;
+
+ g_assert (GSTYLE_IS_COLOR_WIDGET (self));
+
+ update_container_parent_informations (self);
+}
+
+static void
+gstyle_color_widget_realize (GtkWidget *widget)
+{
+ GtkAllocation allocation;
+ GdkWindow *window;
+ GdkWindowAttr attributes = {0};
+
+ g_assert (GTK_IS_WIDGET (widget));
+
+ gtk_widget_get_allocation (widget, &allocation);
+ gtk_widget_set_realized (widget, TRUE);
+
+ attributes.x = allocation.x;
+ attributes.y = allocation.y;
+ attributes.width = allocation.width;
+ attributes.height = allocation.height;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.event_mask = gtk_widget_get_events (widget)
+ | GDK_BUTTON_MOTION_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK;
+
+ window = gdk_window_new (gtk_widget_get_parent_window (widget),
+ &attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL);
+ gtk_widget_set_window (widget, g_object_ref (window));
+ gtk_widget_register_window (widget, window);
+ gdk_window_show (window);
+}
+
+static void
+gstyle_color_widget_finalize (GObject *object)
+{
+ GstyleColorWidget *self = GSTYLE_COLOR_WIDGET (object);
+
+ if (self->color)
+ gstyle_color_widget_disconnect_color (self);
+
+ g_clear_object (&self->dnd_window);
+ g_clear_object (&self->color);
+ g_clear_object (&self->default_provider);
+ cairo_pattern_destroy (self->checkered_pattern);
+ gtk_target_list_unref (self->target_list);
+
+ G_OBJECT_CLASS (gstyle_color_widget_parent_class)->finalize (object);
+}
+
+static void
+gstyle_color_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleColorWidget *self = GSTYLE_COLOR_WIDGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_COLOR:
+ g_value_set_object (value, gstyle_color_widget_get_color (self));
+ break;
+
+ case PROP_DND_LOCK:
+ g_value_set_flags (value, self->dnd_lock);
+ break;
+
+ case PROP_NAME_VISIBLE:
+ g_value_set_boolean (value, gstyle_color_widget_get_name_visible (self));
+ break;
+
+ case PROP_FALLBACK_NAME_KIND:
+ g_value_set_enum (value, gstyle_color_widget_get_fallback_name_kind (self));
+ break;
+
+ case PROP_FALLBACK_NAME_VISIBLE:
+ g_value_set_boolean (value, gstyle_color_widget_get_fallback_name_visible (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_color_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleColorWidget *self = GSTYLE_COLOR_WIDGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_COLOR:
+ gstyle_color_widget_set_color (self, g_value_get_object (value));
+ break;
+
+ case PROP_DND_LOCK:
+ self->dnd_lock = g_value_get_flags (value);
+ break;
+
+ case PROP_NAME_VISIBLE:
+ gstyle_color_widget_set_name_visible (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_FALLBACK_NAME_KIND:
+ gstyle_color_widget_set_fallback_name_kind (self, g_value_get_enum (value));
+ break;
+
+ case PROP_FALLBACK_NAME_VISIBLE:
+ gstyle_color_widget_set_fallback_name_visible (self, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_color_widget_class_init (GstyleColorWidgetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkContainerClass *container_class = (GtkContainerClass*) klass;
+
+ object_class->finalize = gstyle_color_widget_finalize;
+ object_class->get_property = gstyle_color_widget_get_property;
+ object_class->set_property = gstyle_color_widget_set_property;
+
+ widget_class->size_allocate = gstyle_color_widget_size_allocate;
+ widget_class->realize = gstyle_color_widget_realize;
+ widget_class->get_preferred_width = gstyle_color_widget_get_preferred_width;
+ widget_class->get_preferred_height = gstyle_color_widget_get_preferred_height;
+ widget_class->hierarchy_changed = gstyle_color_widget_hierarchy_changed;
+ widget_class->draw = gstyle_color_widget_draw;
+
+ widget_class->drag_begin = gstyle_color_widget_on_drag_begin;
+ widget_class->drag_end = gstyle_color_widget_on_drag_end;
+ widget_class->drag_failed = gstyle_color_widget_on_drag_failed;
+ widget_class->drag_data_get = gstyle_color_widget_on_drag_data_get;
+
+ widget_class->drag_motion = gstyle_color_widget_on_drag_motion;
+ widget_class->drag_leave = gstyle_color_widget_on_drag_leave;
+ widget_class->drag_drop = gstyle_color_widget_on_drag_drop;
+ widget_class->drag_data_received = gstyle_color_widget_on_drag_data_received;
+
+ properties[PROP_COLOR] =
+ g_param_spec_object ("color",
+ "color",
+ "A GstyleColor to use name and color from",
+ GSTYLE_TYPE_COLOR,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_NAME_VISIBLE] =
+ g_param_spec_boolean ("name-visible",
+ "name-visible",
+ "set the color name visibility",
+ TRUE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_FALLBACK_NAME_VISIBLE] =
+ g_param_spec_boolean ("fallback-name-visible",
+ "fallback-name-visible",
+ "set the fallback name visibility",
+ TRUE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_FALLBACK_NAME_KIND] =
+ g_param_spec_enum ("fallback-name-kind",
+ "fallback-name-kind",
+ "if there's no name, the fallback kind displayed",
+ GSTYLE_TYPE_COLOR_KIND,
+ GSTYLE_COLOR_KIND_ORIGINAL,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DND_LOCK] =
+ g_param_spec_flags ("dnd-lock",
+ "dnd-lock",
+ "Dnd lockability",
+ GSTYLE_TYPE_COLOR_WIDGET_DND_LOCK_FLAGS,
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_NONE,
+ (G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_container_class_handle_border_width (container_class);
+ gtk_widget_class_set_css_name (widget_class, "gstylecolorwidget");
+}
+
+static void
+gstyle_color_widget_init (GstyleColorWidget *self)
+{
+ GtkStyleContext *context;
+ GtkWidget *widget = GTK_WIDGET (self);
+
+ static const GtkTargetEntry dnd_targets [] = {
+ {"GSTYLE_COLOR_WIDGET", GTK_TARGET_SAME_APP, 0},
+ {"application/x-color", 0, 0},
+ };
+
+ gtk_widget_set_has_window (GTK_WIDGET (self), TRUE);
+
+ self->label = GTK_LABEL (g_object_new (GTK_TYPE_LABEL,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "visible", TRUE,
+ "halign", GTK_ALIGN_CENTER,
+ "valign", GTK_ALIGN_CENTER,
+ NULL));
+
+ gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->label));
+ self->is_name_visible = TRUE;
+ self->is_fallback_name_visible = TRUE;
+ self->fallback_name_kind = GSTYLE_COLOR_KIND_RGB_HEX6;
+
+ self->checkered_pattern = gstyle_utils_get_checkered_pattern ();
+
+ gtk_widget_set_valign (widget, GTK_ALIGN_FILL);
+ gtk_widget_set_halign (widget, GTK_ALIGN_FILL);
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_widget_set_vexpand (widget, TRUE);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ self->default_provider = gstyle_css_provider_init_default (gtk_style_context_get_screen (context));
+
+ self->target_list = gtk_target_list_new (dnd_targets, G_N_ELEMENTS (dnd_targets));
+ gtk_target_list_add_text_targets (self->target_list, 0);
+
+ gtk_drag_source_set (widget, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY);
+ gtk_drag_source_set_target_list (widget, self->target_list);
+
+ gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
+ gtk_drag_dest_set_target_list (widget, self->target_list);
+ gtk_drag_dest_set_track_motion (GTK_WIDGET (self), TRUE);
+
+ update_container_parent_informations (self);
+
+ self->drag_gesture = gtk_gesture_multi_press_new (widget);
+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->drag_gesture),
+ GDK_BUTTON_PRIMARY);
+ gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->drag_gesture),
+ GTK_PHASE_BUBBLE);
+
+ g_signal_connect (self->drag_gesture, "pressed",
+ G_CALLBACK (gstyle_color_widget_multipress_gesture_pressed), widget);
+}
+
+GType
+gstyle_color_widget_dnd_lock_flags_get_type (void)
+{
+ static GType type_id;
+ static const GFlagsValue values[] = {
+ { GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_NONE, "GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_NONE", "none" },
+ { GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_KIND, "GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_KIND", "kind" },
+ { GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_NAME, "GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_NAME", "name" },
+ { GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_ALPHA, "GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_ALPHA", "alpha" },
+ { GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_COLOR, "GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_COLOR", "COLOR" },
+ { GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_ALL, "GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_ALL", "all" },
+ { 0 }
+ };
+
+ if (g_once_init_enter (&type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_flags_register_static ("GstyleColorWidgetDndLockFlags", values);
+ g_once_init_leave (&type_id, _type_id);
+ }
+
+ return type_id;
+}
diff --git a/contrib/gstyle/gstyle-color-widget.h b/contrib/gstyle/gstyle-color-widget.h
new file mode 100644
index 0000000..f6a4ae6
--- /dev/null
+++ b/contrib/gstyle/gstyle-color-widget.h
@@ -0,0 +1,71 @@
+/* gstyle-color-widget.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_WIDGET_H
+#define GSTYLE_COLOR_WIDGET_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-color.h"
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_NONE = 0,
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_KIND = 1 << 0,
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_NAME = 1 << 1,
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_ALPHA = 1 << 2,
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_COLOR = 1 << 3,
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_ALL =
+ (GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_KIND |
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_NAME |
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_ALPHA |
+ GSTYLE_COLOR_WIDGET_DND_LOCK_FLAGS_COLOR)
+} GstyleColorWidgetDndLockFlags;
+
+#define GSTYLE_TYPE_COLOR_WIDGET (gstyle_color_widget_get_type())
+#define GSTYLE_TYPE_COLOR_WIDGET_DND_LOCK_FLAGS (gstyle_color_widget_dnd_lock_flags_get_type ())
+
+G_DECLARE_FINAL_TYPE (GstyleColorWidget, gstyle_color_widget, GSTYLE, COLOR_WIDGET, GtkBin)
+
+GType gstyle_color_widget_dnd_lock_flags_get_type (void);
+
+GstyleColorWidget *gstyle_color_widget_new (void);
+GstyleColorWidget *gstyle_color_widget_copy (GstyleColorWidget *self);
+GstyleColorWidget *gstyle_color_widget_new_with_color (GstyleColor *color);
+
+gboolean gstyle_color_widget_get_name_visible (GstyleColorWidget *self);
+gboolean gstyle_color_widget_get_fallback_name_visible (GstyleColorWidget *self);
+GstyleColorKind gstyle_color_widget_get_fallback_name_kind (GstyleColorWidget *self);
+GstyleColor *gstyle_color_widget_get_color (GstyleColorWidget *self);
+
+void gstyle_color_widget_set_fallback_name_visible (GstyleColorWidget *self,
+ gboolean visible);
+void gstyle_color_widget_set_fallback_name_kind (GstyleColorWidget *self,
+ GstyleColorKind kind);
+void gstyle_color_widget_set_name_visible (GstyleColorWidget *self,
+ gboolean visible);
+void gstyle_color_widget_set_color (GstyleColorWidget *self,
+ GstyleColor *color);
+
+G_END_DECLS
+
+#endif /* GSTYLE_TYPE_COLOR_WIDGET */
+
diff --git a/contrib/gstyle/gstyle-color.c b/contrib/gstyle/gstyle-color.c
new file mode 100644
index 0000000..bc35219
--- /dev/null
+++ b/contrib/gstyle/gstyle-color.c
@@ -0,0 +1,1305 @@
+/* gstyle-color.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This file 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 file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-color"
+
+#include <math.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <fuzzy.h>
+
+#include "gstyle-private.h"
+#include "gstyle-colorlexer.h"
+#include "gstyle-color-convert.h"
+#include "gstyle-color-item.h"
+#include "gstyle-color-predefined.h"
+
+#include "gstyle-color.h"
+
+/**
+ * SECTION: GstyleColor
+ * @title: Color container.
+ * @short_description: A color container.
+ *
+ * The purpose of GstyleColor is having a container for
+ * color strings representation.
+ */
+
+/* TODO: add operations on the color: darker lighter mix...*/
+
+#define GET_COMP(ar, i) (g_array_index(ar, GstyleColorComponent, i))
+#define FMOD_360(val) ({ double a; return ((a = x / 360.0)-(int)a)*y; })
+
+#define GSTYLE_COLOR_FUZZY_SEARCH_MAX_LEN 20
+
+typedef struct
+{
+ gdouble value;
+ GstyleColorUnit unit;
+} GstyleColorComponent;
+
+struct _GstyleColor
+{
+ GObject parent_instance;
+
+ GstyleColorKind kind;
+ gchar *name;
+ gint name_index;
+ GdkRGBA rgba;
+};
+
+typedef enum
+{
+ RANGE_PERCENT,
+ RANGE_PERCENT_OR_1MAX,
+ RANGE_PERCENT_OR_255MAX
+} ComponentRange;
+
+G_DEFINE_TYPE (GstyleColor, gstyle_color, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_KIND,
+ PROP_RGBA,
+ PROP_NAME,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static gchar TRUNCATE_BUF[6];
+
+/**
+ * gstyle_color_to_hsla:
+ * @self: A #GstyleColor
+ * @hue: (out): The hue component of a hsla color in range [0.0-360.0[
+ * @saturation: (out): The saturation component of a hsla color in range [0.0-100.0]
+ * @lightness: (out): The lightness component of a hsla color in range [0.0-100.0]
+ * @alpha: (out) (nullable): The alpha component of a hsla color in range [0.0-100.0]
+ *
+ * Get the hsla components from a #GstyleColor.
+ *
+ */
+void
+gstyle_color_to_hsla (GstyleColor *self,
+ gdouble *hue,
+ gdouble *saturation,
+ gdouble *lightness,
+ gdouble *alpha)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR (self));
+ g_return_if_fail (hue != NULL);
+ g_return_if_fail (saturation != NULL);
+ g_return_if_fail (lightness != NULL);
+
+ gstyle_color_convert_rgb_to_hsl (&self->rgba, hue, saturation, lightness);
+ if (alpha != NULL)
+ *alpha = self->rgba.alpha;
+}
+
+static gchar *
+truncate_trailing_zeros (gdouble number)
+{
+ gint c = g_snprintf(TRUNCATE_BUF, 6, "%.2f", number);
+
+ --c;
+ while(TRUNCATE_BUF[c] == '0')
+ c--;
+
+ if (TRUNCATE_BUF[c] == '.')
+ c--;
+
+ TRUNCATE_BUF[c + 1] = '\0';
+ return TRUNCATE_BUF;
+}
+
+/**
+ * gstyle_color_to_string:
+ * @self: A #GstyleColor
+ * @kind: The kind of representation as a #GstyleColorKind
+ *
+ * Get the string representation of a #GstyleColor.
+ *
+ * Notice that:
+ * - asking for an HEX3 format take only the 4 left bits of each components into account.
+ * - asking for a predefined named color format return the closest color according to
+ * CIE2000 deltaE calculation, unless the original kind is already a named color.
+ *
+ * Returns: A null-terminated string.
+ *
+ */
+gchar *
+gstyle_color_to_string (GstyleColor *self,
+ GstyleColorKind kind)
+{
+ gchar *string = NULL;
+ gchar *alpha_str = NULL;
+ gdouble hue = 0.0;
+ gdouble saturation = 0.0;
+ gdouble lightness = 0.0;
+ guint red = 0.0;
+ guint green = 0.0;
+ guint blue = 0.0;
+
+ g_return_val_if_fail (GSTYLE_IS_COLOR (self), NULL);
+
+ if (kind == GSTYLE_COLOR_KIND_ORIGINAL)
+ kind = self->kind;
+
+ switch (kind)
+ {
+ case GSTYLE_COLOR_KIND_RGB_HEX3:
+ case GSTYLE_COLOR_KIND_RGB_HEX6:
+ case GSTYLE_COLOR_KIND_RGB:
+ case GSTYLE_COLOR_KIND_RGBA:
+ red = (guint)(0.5 + CLAMP (self->rgba.red, 0.0, 1.0) * 255.0);
+ green = (guint)(0.5 + CLAMP (self->rgba.green, 0.0, 1.0) * 255.0);
+ blue = (guint)(0.5 + CLAMP (self->rgba.blue, 0.0, 1.0) * 255.0);
+ break;
+
+ case GSTYLE_COLOR_KIND_RGB_PERCENT:
+ case GSTYLE_COLOR_KIND_RGBA_PERCENT:
+ red = (guint)(0.5 + CLAMP (self->rgba.red, 0.0, 1.0) * 100.0);
+ green = (guint)(0.5 + CLAMP (self->rgba.green, 0.0, 1.0) * 100.0);
+ blue = (guint)(0.5 + CLAMP (self->rgba.blue, 0.0, 1.0) * 100.0);
+ break;
+
+ case GSTYLE_COLOR_KIND_HSL:
+ case GSTYLE_COLOR_KIND_HSLA:
+ gstyle_color_convert_rgb_to_hsl (&self->rgba, &hue, &saturation, &lightness);
+ break;
+
+ case GSTYLE_COLOR_KIND_PREDEFINED:
+ /* TODO: get closest color using deltaE formula unless the original kind is already a named color */
+ break;
+
+ case GSTYLE_COLOR_KIND_UNKNOW:
+ g_warning ("UNKNOW #GstyleColorKind is not meant to be used for color string output");
+ return NULL;
+ break;
+
+ case GSTYLE_COLOR_KIND_ORIGINAL:
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* alpha_str is a pointer to a static buffer, DO NOT free it */
+ alpha_str = truncate_trailing_zeros (self->rgba.alpha);
+
+ switch (kind)
+ {
+ case GSTYLE_COLOR_KIND_RGB_HEX3:
+ string = g_strdup_printf ("#%01X%01X%01X", red / 16, green / 16, blue / 16);
+ break;
+
+ case GSTYLE_COLOR_KIND_RGB_HEX6:
+ string = g_strdup_printf ("#%02X%02X%02X", red, green, blue);
+ break;
+
+ case GSTYLE_COLOR_KIND_RGB:
+ string = g_strdup_printf ("rgb(%i, %i, %i)", red, green, blue);
+ break;
+
+ case GSTYLE_COLOR_KIND_RGBA:
+ string = g_strdup_printf ("rgba(%i, %i, %i, %s)", red, green, blue, alpha_str);
+ break;
+
+ case GSTYLE_COLOR_KIND_RGB_PERCENT:
+ string = g_strdup_printf ("rgb(%i%%, %i%%, %i%%)", red, green, blue);
+ break;
+
+ case GSTYLE_COLOR_KIND_RGBA_PERCENT:
+ string = g_strdup_printf ("rgba(%i%%, %i%%, %i%%, %s)", red, green, blue, alpha_str);
+ break;
+
+ case GSTYLE_COLOR_KIND_HSL:
+ string = g_strdup_printf ("hsl(%i, %i%%, %i%%)", (gint)hue, (gint)(saturation + 0.5), (gint)(lightness
+ 0.5));
+ break;
+
+ case GSTYLE_COLOR_KIND_HSLA:
+ string = g_strdup_printf ("hsla(%i, %i%%, %i%%, %s)", (gint)hue, (gint)(saturation + 0.5),
(gint)(lightness + 0.5), alpha_str);
+ break;
+
+ case GSTYLE_COLOR_KIND_PREDEFINED:
+ if (self->name_index != -1)
+ string = g_strdup (predefined_colors_table [self->name_index].name);
+ else
+ {
+ /* TODO: get similar color using cie2000 */
+ }
+ break;
+
+ case GSTYLE_COLOR_KIND_ORIGINAL:
+ case GSTYLE_COLOR_KIND_UNKNOW:
+ default:
+ g_assert_not_reached ();
+ }
+
+ return string;
+}
+
+/**
+ * gstyle_color_get_rgba:
+ * @self: A #GstyleColor
+ *
+ * Get a #GdkRGBA object from a #GstyleColor.
+ *
+ * Returns: A #GdkRGBA.
+ *
+ */
+GdkRGBA *
+gstyle_color_get_rgba (GstyleColor *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR (self), NULL);
+
+ return gdk_rgba_copy (&self->rgba);
+}
+
+/**
+ * gstyle_color_fill_rgba:
+ * @self: A #GstyleColor
+ * @rgba: (out): the #GdkRGBA to fill in
+ *
+ * Fill a #GdkRGBA object from a #GstyleColor.
+ *
+ */
+void
+gstyle_color_fill_rgba (GstyleColor *self,
+ GdkRGBA *rgba)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR (self));
+ g_return_if_fail (rgba != NULL);
+
+ rgba->red = self->rgba.red;
+ rgba->green = self->rgba.green;
+ rgba->blue = self->rgba.blue;
+ rgba->alpha = self->rgba.alpha;
+}
+
+/**
+ * gstyle_color_parse:
+ * @string: A null terminated string to parse.
+ *
+ * Get an pointer array of #GstyleColorItem indicating the color and position.
+ * of color strings in the parsed string.
+ *
+ * Returns: (nullable) (element-type GstyleColorItem) (transfer full): A newly allocated
+ * #GPtrArray containing #GstyleColorItem elements. This should be freed when
+ * the caller is done with it using g_ptr_array_free().
+ *
+ */
+GPtrArray *
+gstyle_color_parse (const gchar *string)
+{
+ GPtrArray *ar;
+ gint n = 0;
+
+ g_return_val_if_fail (!gstyle_str_empty0 (string), NULL);
+
+ /* TODO: try doing full parsing in the parser */
+
+ /* First pass to get color strings */
+ ar = gstyle_colorlexer_parse (string);
+ while (n < ar->len)
+ {
+ g_autofree gchar *str = NULL;
+ GstyleColorItem *item;
+ GstyleColor *color;
+
+ item = g_ptr_array_index (ar, n);
+ str = g_strndup (string + gstyle_color_item_get_start (item),
+ gstyle_color_item_get_len (item));
+
+ /* Second pass to get rgba and kind */
+ color = gstyle_color_new_from_string (NULL, str);
+ if (color != NULL)
+ {
+ gstyle_color_item_set_color (item, color);
+ g_object_unref (color);
+ ++n;
+ }
+ else
+ g_ptr_array_remove_index (ar, n);
+ }
+
+ return ar;
+}
+
+/* TODO: support for a minus in front of values */
+/* format is [0-9]*.*[0-9]* */
+static gboolean
+str_to_float (GstyleColorScanner *s,
+ gdouble *number)
+{
+ const gchar *cursor;
+ guint left = 0;
+ guint right = 0;
+ guint precision = 0;
+
+ cursor = s->start = s->cursor;
+
+ if (g_ascii_isdigit (*cursor) || *cursor == '.')
+ {
+ while (g_ascii_isdigit (*cursor))
+ {
+ left = left * 10 + (*cursor - '0');
+ ++cursor;
+ }
+
+ if (*cursor != '.')
+ goto finish;
+ else
+ ++cursor;
+
+ while (g_ascii_isdigit (*cursor))
+ {
+ right = right * 10 + (*cursor - '0');
+ ++precision;
+ ++cursor;
+ }
+ }
+ else
+ {
+ s->cursor = cursor;
+ return FALSE;
+ }
+
+finish:
+ s->cursor = cursor;
+
+ if ((cursor - s->start) == 1 && *(s->start) == '.')
+ return FALSE;
+
+ *number = left + (right / pow (10, precision));
+ return TRUE;
+}
+
+/* parse hex_len hexadecimal digits into value */
+static gboolean
+get_hex_digit (const gchar *str,
+ guint hex_len,
+ guint *value)
+{
+ gint xdigit;
+ gint num = 0;
+
+ for (; hex_len > 0; --hex_len)
+ {
+ num <<= 4;
+
+ xdigit = g_ascii_xdigit_value (*str);
+ if (xdigit == -1)
+ return FALSE;
+
+ num += xdigit;
+ ++str;
+ }
+
+ *value = num;
+ return TRUE;
+}
+
+/* String need at least to start with '#'
+ * format #[0-9A-Fa-f]{3} or #[0-9A-Fa-f]{6}
+ */
+static gboolean
+_parse_hex_string (const gchar *str,
+ GdkRGBA *rgba,
+ GstyleColorKind *kind)
+{
+ gint len;
+ guint r, g, b;
+
+ g_return_val_if_fail (*str == '#', FALSE);
+
+ rgba->alpha = 1.0;
+
+ len = strlen (str);
+ if (len == 4)
+ {
+ if (get_hex_digit (str + 1, 1, &r) &&
+ get_hex_digit (str + 2, 1, &g) &&
+ get_hex_digit (str + 3, 1, &b))
+ {
+ rgba->red = (r | r << 4) / 255.0;
+ rgba->green = (g | g << 4) / 255.0;
+ rgba->blue = (b | b << 4) / 255.0;
+
+ *kind = GSTYLE_COLOR_KIND_RGB_HEX3;
+ return TRUE;
+ }
+ }
+ else if (len == 7)
+ {
+ if (get_hex_digit (str + 1, 2, &r) &&
+ get_hex_digit (str + 3, 2, &g) &&
+ get_hex_digit (str + 5, 2, &b))
+ {
+ rgba->red = r / 255.0;
+ rgba->green = g / 255.0;
+ rgba->blue = b / 255.0;
+
+ *kind = GSTYLE_COLOR_KIND_RGB_HEX6;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static inline gboolean
+check_char (GstyleColorScanner *s,
+ gchar ch)
+{
+ if (*(s->cursor) == ch)
+ {
+ ++(s->cursor);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static inline gboolean
+skip_spaces (GstyleColorScanner *s)
+{
+ const gchar *cursor = s->cursor;
+ gunichar c;
+
+ while (g_unichar_isspace (c = g_utf8_get_char (cursor)))
+ cursor = g_utf8_next_char (cursor);
+
+ s->cursor = cursor;
+ return (c != '\0');
+}
+
+static GArray *
+parse_components (GstyleColorScanner *s)
+{
+ GstyleColorComponent comp;
+ gdouble value;
+ gboolean need_more = FALSE;
+ GArray *ar = g_array_sized_new (FALSE, FALSE, sizeof (GstyleColorComponent), 4);
+
+ skip_spaces (s);
+ while (str_to_float (s, &value))
+ {
+ need_more = FALSE;
+ comp.unit = GSTYLE_COLOR_UNIT_NONE;
+
+ if (check_char (s, '%'))
+ comp.unit = GSTYLE_COLOR_UNIT_PERCENT;
+
+ comp.value = value;
+ g_array_append_val (ar, comp);
+
+ skip_spaces (s);
+ if (check_char (s, ','))
+ {
+ skip_spaces (s);
+ need_more = TRUE;
+ }
+ else if (!(*s->cursor == ')'))
+ {
+ g_array_free (ar, TRUE);
+ return NULL;
+ }
+ }
+
+ if (need_more == TRUE)
+ {
+ g_array_free (ar, TRUE);
+ return NULL;
+ }
+ else
+ return ar;
+}
+
+static gboolean
+convert_component (GstyleColorComponent comp,
+ ComponentRange range,
+ gdouble *number)
+{
+ gdouble n = comp.value;
+
+ if (comp.unit == GSTYLE_COLOR_UNIT_PERCENT)
+ {
+ *number = CLAMP (n, 0.0, 100.0) / 100.0;
+ }
+ else
+ {
+ if (range == RANGE_PERCENT)
+ return FALSE;
+
+ if (range == RANGE_PERCENT_OR_1MAX)
+ *number = CLAMP (n, 0.0, 1.0);
+ else if (range == RANGE_PERCENT_OR_255MAX)
+ *number = CLAMP (n, 0.0, 255.0) / 255.0;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+convert_hue_component (GstyleColorComponent comp,
+ gdouble *hue)
+{
+ gdouble num = comp.value;
+
+ if (comp.unit == GSTYLE_COLOR_UNIT_PERCENT)
+ return FALSE;
+
+ if (num == 360.0)
+ num = 0.0;
+ else if (num > 360.0)
+ num = fmod (num, 360.0);
+ else if (num < 0.0)
+ num = fmod (num, 360.0) + 360.0;
+
+ *hue = num;
+ return TRUE;
+}
+
+/* String need at least to start with 'rgb' */
+static gboolean
+_parse_rgba_string (const gchar *string,
+ GdkRGBA *rgba,
+ GstyleColorKind *kind)
+{
+ GstyleColorScanner s;
+ g_autoptr (GArray) ar = NULL;
+ gboolean has_alpha;
+ gboolean is_percent;
+ gboolean ret;
+
+ g_return_val_if_fail (*string == 'r' && *(string + 1) == 'g' && *(string + 2) == 'b', FALSE);
+
+ s.start = string;
+ s.cursor = string + 3;
+
+ has_alpha = check_char (&s, 'a');
+ if (check_char (&s, '(') && (ar = parse_components (&s)))
+ {
+ is_percent = (GET_COMP (ar, 0).unit == GSTYLE_COLOR_UNIT_PERCENT);
+ if ((ar->len == 3 && !has_alpha) || (ar->len == 4 && has_alpha))
+ {
+ ret = convert_component (GET_COMP (ar, 0), RANGE_PERCENT_OR_255MAX, &rgba->red) &&
+ convert_component (GET_COMP (ar, 1), RANGE_PERCENT_OR_255MAX, &rgba->green) &&
+ convert_component (GET_COMP (ar, 2), RANGE_PERCENT_OR_255MAX, &rgba->blue);
+
+ if (has_alpha)
+ {
+ ret = ret && convert_component (GET_COMP (ar, 3), RANGE_PERCENT_OR_1MAX, &rgba->alpha);
+ *kind = is_percent ? GSTYLE_COLOR_KIND_RGBA_PERCENT : GSTYLE_COLOR_KIND_RGBA;
+ }
+ else
+ {
+ rgba->alpha = 1.0;
+ *kind = is_percent ? GSTYLE_COLOR_KIND_RGB_PERCENT : GSTYLE_COLOR_KIND_RGB;
+ }
+
+ if (ret && check_char (&s, ')'))
+ return TRUE;
+ }
+ }
+
+ *kind = GSTYLE_COLOR_KIND_UNKNOW;
+
+ return FALSE;
+}
+
+/* String need at least to start with 'hsl' */
+static gboolean
+_parse_hsla_string (const gchar *string,
+ GdkRGBA *rgba,
+ GstyleColorKind *kind)
+{
+ GstyleColorScanner s;
+ g_autoptr (GArray) ar = NULL;
+ gdouble hue;
+ gdouble saturation;
+ gdouble lightness;
+ gboolean has_alpha = FALSE;
+ gboolean ret;
+
+ g_return_val_if_fail (*string == 'h' && *(string + 1) == 's' && *(string + 2) == 'l', FALSE);
+
+ s.start = string;
+ s.cursor = string + 3;
+
+ has_alpha = check_char (&s, 'a');
+ if (check_char (&s, '(') && (ar = parse_components (&s)))
+ {
+ if ((ar->len == 3 && !has_alpha) || (ar->len == 4 && has_alpha))
+ {
+ ret = convert_hue_component (GET_COMP (ar, 0), &hue) &&
+ convert_component (GET_COMP (ar, 1), RANGE_PERCENT, &saturation) &&
+ convert_component (GET_COMP (ar, 2), RANGE_PERCENT, &lightness);
+
+ if (has_alpha)
+ {
+ ret = ret && convert_component (GET_COMP (ar, 3), RANGE_PERCENT_OR_1MAX, &rgba->alpha);
+ *kind = GSTYLE_COLOR_KIND_HSLA;
+ }
+ else
+ {
+ rgba->alpha = 1.0;
+ *kind = GSTYLE_COLOR_KIND_HSL;
+ }
+
+ if (ret && check_char (&s, ')'))
+ {
+ gstyle_color_convert_hsl_to_rgb (hue, saturation, lightness, rgba);
+ return TRUE;
+ }
+ }
+ }
+
+ *kind = GSTYLE_COLOR_KIND_UNKNOW;
+
+ return FALSE;
+}
+
+/* TODO: add a public func to init so we can control the initial starting time ? */
+static Fuzzy *
+_init_predefined_table (void)
+{
+ static Fuzzy *predefined_table;
+ NamedColor *item;
+
+ if (predefined_table == NULL)
+ {
+ predefined_table = fuzzy_new (TRUE);
+
+ fuzzy_begin_bulk_insert (predefined_table);
+ for (guint i = 0; i < G_N_ELEMENTS (predefined_colors_table); ++i)
+ {
+ item = &predefined_colors_table [i];
+ item->index = i;
+ fuzzy_insert (predefined_table, item->name, (gpointer)item);
+ }
+
+ fuzzy_end_bulk_insert (predefined_table);
+ }
+
+ return predefined_table;
+}
+
+static gboolean
+_parse_predefined_color (const gchar *color_string,
+ GdkRGBA *rgba,
+ gint *name_index)
+{
+ g_autoptr (GArray) results = NULL;
+ NamedColor *item = NULL;
+ FuzzyMatch *match;
+ gint len;
+ Fuzzy *predefined_table = _init_predefined_table ();
+
+ results = fuzzy_match (predefined_table, color_string, 10);
+ len = results->len;
+ for (gint i = 0; i < len; ++i)
+ {
+ match = &g_array_index (results, FuzzyMatch, i);
+ if (g_strcmp0 (color_string, match->key) == 0)
+ {
+ item = match->value;
+ *name_index = (gint)(item - predefined_colors_table);
+ rgba->alpha = 1.0;
+ rgba->red = item->red /255.0;
+ rgba->green = item->green / 255.0;
+ rgba->blue = item->blue / 255.0;
+
+ return TRUE;
+ }
+ }
+
+ *name_index = -1;
+ return FALSE;
+}
+
+/**
+ * gstyle_color_fuzzy_parse_color_string:
+ * @color_string: color name to search for
+ *
+ * Returns: (transfer full) (element-type GstyleColor): A #GPtrArray of #GstyleColor for a fuzzy search.
+ */
+GPtrArray *
+gstyle_color_fuzzy_parse_color_string (const gchar *color_string)
+{
+ g_autoptr (GArray) fuzzy_results = NULL;
+ GPtrArray *results;
+ FuzzyMatch *match;
+ NamedColor *item;
+ GstyleColor *color;
+ GdkRGBA rgba;
+ gint len;
+
+ Fuzzy *predefined_table = _init_predefined_table ();
+
+ results = g_ptr_array_new_with_free_func (g_object_unref);
+ fuzzy_results = fuzzy_match (predefined_table, color_string, GSTYLE_COLOR_FUZZY_SEARCH_MAX_LEN);
+ len = MIN (GSTYLE_COLOR_FUZZY_SEARCH_MAX_LEN, fuzzy_results->len);
+ for (gint i = 0; i < len; ++i)
+ {
+ match = &g_array_index (fuzzy_results, FuzzyMatch, i);
+ item = match->value;
+ rgba.red = item->red / 255.0;
+ rgba.green = item->green / 255.0;
+ rgba.blue = item->blue / 255.0;
+ rgba.alpha = 1.0;
+
+ color = gstyle_color_new_from_rgba (g_strdup (match->key), GSTYLE_COLOR_KIND_PREDEFINED, &rgba);
+ color->name_index = item->index;
+
+ g_ptr_array_add (results, color);
+ }
+
+ return results;
+}
+
+/* TODO: add hsv/hwb colorspace */
+static gboolean
+_parse_color_string (const gchar *color_string,
+ GdkRGBA *rgba,
+ GstyleColorKind *kind,
+ gint *name_index)
+{
+ gboolean ret;
+
+ g_assert (!gstyle_str_empty0 (color_string));
+ g_assert (rgba != NULL);
+
+ *name_index = -1;
+
+ if (color_string[0] == '#')
+ ret = _parse_hex_string (color_string, rgba, kind);
+ else if (g_str_has_prefix (color_string, "rgb"))
+ ret = _parse_rgba_string (color_string, rgba, kind);
+ else if (g_str_has_prefix (color_string, "hsl"))
+ ret = _parse_hsla_string (color_string, rgba, kind);
+ else
+ {
+ if ((ret = _parse_predefined_color (color_string, rgba, name_index)))
+ *kind =GSTYLE_COLOR_KIND_PREDEFINED;
+ else
+ *kind =GSTYLE_COLOR_KIND_UNKNOW;
+ }
+
+ return ret;
+}
+
+/**
+ * gstyle_color_parse_color_string:
+ * @color_string: A null-terminated color string to parse
+ * @rgba: (out): The #GdkRGBA to fill in
+ * @kind: (out): The kind of representation as a #GstyleColorKind
+ *
+ * Get a #GdkRGBA and a #GstyleColorKind from a color string.
+ * If you want to keep the args unit format,
+ * use gstyle_color_new_from_string instead.
+ *
+ * Returns: %TRUE on success, %FALSE if an error occurred.
+ *
+ */
+gboolean
+gstyle_color_parse_color_string (const gchar *color_string,
+ GdkRGBA *rgba,
+ GstyleColorKind *kind)
+{
+ G_GNUC_UNUSED gint name_index;
+ gboolean ret;
+
+ g_return_val_if_fail (!gstyle_str_empty0 (color_string), FALSE);
+ g_return_val_if_fail (rgba != NULL, FALSE);
+
+ ret = _parse_color_string (color_string, rgba, kind, &name_index);
+ if (*kind == GSTYLE_COLOR_KIND_UNKNOW)
+ *kind = GSTYLE_COLOR_KIND_RGB_HEX6;
+
+ return ret;
+}
+
+/**
+ * gstyle_color_fill:
+ * @src_color: Source #GstyleColor
+ * @dst_color: Destination #GstyleColor to fill with @src_color data
+ *
+ * Fill @dst_color with the rgba, name and kind of @src_color.
+ *
+ */
+void
+gstyle_color_fill (GstyleColor *src_color,
+ GstyleColor *dst_color)
+{
+ GdkRGBA rgba;
+
+ g_assert (GSTYLE_IS_COLOR (src_color));
+ g_assert (GSTYLE_IS_COLOR (dst_color));
+
+ gstyle_color_fill_rgba (src_color, &rgba);
+ gstyle_color_set_rgba (dst_color, &rgba);
+ gstyle_color_set_name (dst_color, gstyle_color_get_name (src_color));
+ gstyle_color_set_kind (dst_color, gstyle_color_get_kind (src_color));
+ dst_color->name_index = src_color->name_index;
+}
+
+/**
+ * gstyle_color_copy:
+ * @self: A #GstyleColor
+ *
+ * A full copy of a #GstyleColor.
+ *
+ * Returns: (transfer full): A #GstyleColor.
+ *
+ */
+GstyleColor *
+gstyle_color_copy (GstyleColor *self)
+{
+ GstyleColor *color;
+ GdkRGBA rgba;
+
+ g_return_val_if_fail (GSTYLE_IS_COLOR (self), NULL);
+
+ gstyle_color_fill_rgba (self, &rgba);
+ color = g_object_new (GSTYLE_TYPE_COLOR,
+ "name", gstyle_color_get_name (self),
+ "kind", gstyle_color_get_kind (self),
+ "rgba", &rgba,
+ NULL);
+
+ color->name_index = self->name_index;
+
+ return color;
+}
+
+/**
+ * gstyle_color_new:
+ * @name: (nullable): The name of the color. Can be %NULL
+ * @kind: The kind of representation as a #GstyleColorKind
+ * @red: The red component in a [0-255] range
+ * @green: The green component in a [0-255] range
+ * @blue: The blue component in a [0-255] range
+ * @alpha: The alpha component in a [0-100] range
+ *
+ * A #GstyleColor object from #GstyleColorKind and rgba components.
+ *
+ * Returns: A #GstyleColor.
+ *
+ */
+GstyleColor *
+gstyle_color_new (const gchar *name,
+ GstyleColorKind kind,
+ guint red,
+ guint green,
+ guint blue,
+ guint alpha)
+{
+ GdkRGBA rgba;
+
+ rgba.red = red / 255.0;
+ rgba.green = green / 255.0;
+ rgba.blue = blue / 255.0;
+ rgba.alpha = alpha / 100.0;
+
+ return g_object_new (GSTYLE_TYPE_COLOR,
+ "name", name,
+ "kind", kind,
+ "rgba", &rgba,
+ NULL);
+}
+
+/**
+ * gstyle_color_new_from_rgba:
+ * @name: (nullable): The name of the color. Can be %NULL
+ * @kind: The kind of representation as a #GstyleColorKind
+ * @rgba: A #GdkRGBA
+ *
+ * A #GstyleColor object from a #GstyleColorKind and a #GdkRGBA object.
+ *
+ * Returns: A #GstyleColor.
+ *
+ */
+GstyleColor *
+gstyle_color_new_from_rgba (const gchar *name,
+ GstyleColorKind kind,
+ GdkRGBA *rgba)
+{
+ return g_object_new (GSTYLE_TYPE_COLOR,
+ "name", name,
+ "kind", kind,
+ "rgba", rgba,
+ NULL);
+}
+
+/**
+ * gstyle_color_from_hsla:
+ * @name: (nullable): The name of the color. Can be %NULL
+ * @kind: The kind of representation as a #GstyleColorKind
+ * @hue: The hue component of a hsla color in range [0.0-360.0[
+ * @saturation: The saturation component in range [0.0-1.0]
+ * @lightness: The lightness component in range [0.0-1.0]
+ * @alpha: The alpha component in range [0.0-1.0]
+ *
+ * A #GstyleColor from #GstyleColorKind and rgba components.
+ *
+ * Returns: A #GstyleColor.
+ *
+ */
+GstyleColor *
+gstyle_color_new_from_hsla (const gchar *name,
+ GstyleColorKind kind,
+ gdouble hue,
+ gdouble saturation,
+ gdouble lightness,
+ gdouble alpha)
+{
+ GdkRGBA rgba;
+
+ gstyle_color_convert_hsl_to_rgb (hue, saturation, lightness, &rgba);
+ rgba.alpha = alpha;
+
+ return g_object_new (GSTYLE_TYPE_COLOR,
+ "name", name,
+ "kind", kind,
+ "rgba", &rgba,
+ NULL);
+}
+
+/**
+ * gstyle_color_new_from_string:
+ * @name: (nullable): The name of the color. Can be NULL
+ * @color_string: A null terminated string to parse
+ *
+ * A #GstyleColor object from a color string.
+ *
+ * Returns: A #GstyleColor or %NULL if the string can't be parsed.
+ *
+ */
+GstyleColor *
+gstyle_color_new_from_string (const gchar *name,
+ const gchar *color_string)
+{
+ GstyleColor *self;
+ gint name_index;
+ GdkRGBA rgba;
+ GstyleColorKind kind;
+
+ g_return_val_if_fail (!gstyle_str_empty0 (color_string), NULL);
+
+ if (!_parse_color_string (color_string, &rgba, &kind, &name_index))
+ return NULL;
+
+ if (gstyle_str_empty0 (name))
+ name = NULL;
+
+ self = g_object_new (GSTYLE_TYPE_COLOR,
+ "name", name,
+ "kind", kind,
+ "rgba", &rgba,
+ NULL);
+
+ if (kind == GSTYLE_COLOR_KIND_PREDEFINED)
+ self->name_index = name_index;
+
+ return self;
+}
+
+/**
+ * gstyle_color_get_name:
+ * @self: A #GstyleColor
+ *
+ * Get the name of a #GstyleColor.
+ *
+ * Returns: A string own by the callee.
+ *
+ */
+const gchar *
+gstyle_color_get_name (GstyleColor *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR (self), NULL);
+
+ return self->name;
+}
+
+/**
+ * gstyle_color_set_name:
+ * @self: A #GstyleColor.
+ * @name: (nullable): A string
+ *
+ * Set the name of a #GstyleColor.
+ *
+ */
+void
+gstyle_color_set_name (GstyleColor *self,
+ const gchar *name)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR (self));
+
+ if (g_strcmp0 (name, self->name) != 0)
+ {
+ g_free (self->name);
+ if (!gstyle_str_empty0 (name))
+ self->name = g_strdup (name);
+ else
+ self->name = NULL;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NAME]);
+ }
+}
+
+/**
+ * gstyle_color_get_kind:
+ * @self: A #GstyleColor
+ *
+ * Get the #GstyleColorKind of a #GstyleColor.
+ *
+ * Returns: A #GstyleColorKind.
+ *
+ */
+GstyleColorKind
+gstyle_color_get_kind (GstyleColor *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_COLOR (self), 0);
+
+ return self->kind;
+}
+
+/**
+ * gstyle_color_set_kind:
+ * @self: A #GstyleColor
+ * @kind: A #GstyleColorKind
+ *
+ * Set the #GstyleColorKind of a #GstyleColor.
+ *
+ */
+void
+gstyle_color_set_kind (GstyleColor *self,
+ GstyleColorKind kind)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR (self));
+
+ if (self->kind != kind)
+ {
+ self->kind = kind;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_KIND]);
+ }
+}
+
+/**
+ * gstyle_color_set_rgba:
+ * @self: A #GstyleColor
+ * @rgba: A #GdkRGBA
+ *
+ * Set #GstyleColor color from a #GdkRGBA.
+ *
+ */
+void
+gstyle_color_set_rgba (GstyleColor *self,
+ GdkRGBA *rgba)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR (self));
+ g_return_if_fail (rgba != NULL);
+
+ if (!gdk_rgba_equal (&self->rgba, &rgba))
+ {
+ self->rgba = *rgba;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RGBA]);
+ }
+}
+
+/**
+ * gstyle_color_set_alpha:
+ * @self: A #GstyleColor
+ * @alpha: the new alpha value in [0,1] range
+ *
+ * Set the alpha value the the #GstyleColor.
+ *
+ */
+void
+gstyle_color_set_alpha (GstyleColor *self,
+ gdouble alpha)
+{
+ g_return_if_fail (GSTYLE_IS_COLOR (self));
+
+ if (self->rgba.alpha != alpha)
+ {
+ self->rgba.alpha = alpha;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RGBA]);
+ }
+}
+
+static void
+gstyle_color_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleColor *self = GSTYLE_COLOR (object);
+
+ switch (prop_id)
+ {
+ case PROP_KIND:
+ g_value_set_enum (value, self->kind);
+ break;
+
+ case PROP_RGBA:
+ g_value_set_boxed (value, &self->rgba);
+ break;
+
+ case PROP_NAME:
+ g_value_set_string (value, self->name);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_color_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdkRGBA *rgba;
+ GstyleColor *self = GSTYLE_COLOR (object);
+
+ switch (prop_id)
+ {
+ case PROP_KIND:
+ gstyle_color_set_kind (self, g_value_get_enum (value));
+ break;
+
+ case PROP_RGBA:
+ rgba = g_value_get_boxed (value);
+ gstyle_color_set_rgba (self, rgba);
+ break;
+
+ case PROP_NAME:
+ gstyle_color_set_name (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_color_finalize (GObject *object)
+{
+ GstyleColor *self = GSTYLE_COLOR (object);
+
+ g_clear_pointer (&self->name, g_free);
+
+ G_OBJECT_CLASS (gstyle_color_parent_class)->finalize (object);
+}
+
+static void
+gstyle_color_class_init (GstyleColorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gstyle_color_finalize;
+ object_class->get_property = gstyle_color_get_property;
+ object_class->set_property = gstyle_color_set_property;
+
+ properties [PROP_KIND] =
+ g_param_spec_enum ("kind",
+ "Kind",
+ "The kind of color representation",
+ GSTYLE_TYPE_COLOR_KIND,
+ GSTYLE_COLOR_KIND_RGBA,
+ (G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_RGBA] =
+ g_param_spec_boxed ("rgba",
+ "rgba",
+ "Adress of an GdkRGBA color struct",
+ GDK_TYPE_RGBA,
+ (G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_NAME] =
+ g_param_spec_string ("name",
+ "Color name",
+ "The name of the color.",
+ NULL,
+ (G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gstyle_color_init (GstyleColor *self)
+{
+ self->name_index = -1;
+}
+
+GType
+gstyle_color_kind_get_type (void)
+{
+ static GType type_id;
+ static const GEnumValue values[] = {
+ { GSTYLE_COLOR_KIND_UNKNOW, "GSTYLE_COLOR_KIND_UNKNOW", "unknow" },
+ { GSTYLE_COLOR_KIND_ORIGINAL, "GSTYLE_COLOR_KIND_ORIGINAL", "original" },
+ { GSTYLE_COLOR_KIND_RGB_HEX6, "GSTYLE_COLOR_KIND_RGB_HEX6", "rgbhex6" },
+ { GSTYLE_COLOR_KIND_RGB_HEX3, "GSTYLE_COLOR_KIND_RGB_HEX3", "rgbhex3" },
+ { GSTYLE_COLOR_KIND_RGB, "GSTYLE_COLOR_KIND_RGB", "rgb" },
+ { GSTYLE_COLOR_KIND_RGB_PERCENT, "GSTYLE_COLOR_KIND_RGB_PERCENT", "rgbpercent" },
+ { GSTYLE_COLOR_KIND_RGBA, "GSTYLE_COLOR_KIND_RGBA", "rgba" },
+ { GSTYLE_COLOR_KIND_RGBA_PERCENT, "GSTYLE_COLOR_KIND_RGBA_PERCENT", "rgbapercent" },
+ { GSTYLE_COLOR_KIND_HSL, "GSTYLE_COLOR_KIND_HSL", "hsl" },
+ { GSTYLE_COLOR_KIND_HSLA, "GSTYLE_COLOR_KIND_HSLA", "hsla" },
+ { GSTYLE_COLOR_KIND_PREDEFINED, "GSTYLE_COLOR_KIND_PREDEFINED", "predefined" },
+ { 0 }
+ };
+
+ if (g_once_init_enter (&type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_enum_register_static ("GstyleColorKind", values);
+ g_once_init_leave (&type_id, _type_id);
+ }
+
+ return type_id;
+}
+
+GType
+gstyle_color_unit_get_type (void)
+{
+ static GType type_id;
+ static const GEnumValue values[] = {
+ { GSTYLE_COLOR_UNIT_NONE, "GSTYLE_COLOR_UNIT_NONE", "none" },
+ { GSTYLE_COLOR_UNIT_PERCENT, "GSTYLE_COLOR_UNIT_PERCENT", "percent" },
+ { GSTYLE_COLOR_UNIT_VALUE, "GSTYLE_COLOR_UNIT_VALUE", "value" },
+ { 0 }
+ };
+
+ if (g_once_init_enter (&type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_enum_register_static ("GstyleColorUnit", values);
+ g_once_init_leave (&type_id, _type_id);
+ }
+
+ return type_id;
+}
diff --git a/contrib/gstyle/gstyle-color.h b/contrib/gstyle/gstyle-color.h
new file mode 100644
index 0000000..49d1bed
--- /dev/null
+++ b/contrib/gstyle/gstyle-color.h
@@ -0,0 +1,110 @@
+/* gstyle-color.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This file 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 file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLOR_H
+#define GSTYLE_COLOR_H
+
+#include <glib-object.h>
+#include <gdk/gdk.h>
+
+#include "gstyle-types.h"
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_COLOR (gstyle_color_get_type())
+#define GSTYLE_TYPE_COLOR_KIND (gstyle_color_kind_get_type())
+#define GSTYLE_TYPE_COLOR_UNIT (gstyle_color_unit_get_type())
+
+G_DECLARE_FINAL_TYPE (GstyleColor, gstyle_color, GSTYLE, COLOR, GObject)
+
+typedef enum
+{
+ GSTYLE_COLOR_KIND_UNKNOW,
+ GSTYLE_COLOR_KIND_ORIGINAL,
+ GSTYLE_COLOR_KIND_RGB_HEX6,
+ GSTYLE_COLOR_KIND_RGB_HEX3,
+ GSTYLE_COLOR_KIND_RGB,
+ GSTYLE_COLOR_KIND_RGB_PERCENT,
+ GSTYLE_COLOR_KIND_RGBA,
+ GSTYLE_COLOR_KIND_RGBA_PERCENT,
+ GSTYLE_COLOR_KIND_HSL,
+ GSTYLE_COLOR_KIND_HSLA,
+ GSTYLE_COLOR_KIND_PREDEFINED
+} GstyleColorKind;
+
+typedef enum
+{
+ GSTYLE_COLOR_UNIT_NONE,
+ GSTYLE_COLOR_UNIT_PERCENT,
+ GSTYLE_COLOR_UNIT_VALUE
+} GstyleColorUnit;
+
+GType gstyle_color_kind_get_type (void);
+GType gstyle_color_unit_get_type (void);
+
+GstyleColor *gstyle_color_copy (GstyleColor *self);
+void gstyle_color_fill (GstyleColor *src_color,
+ GstyleColor *dst_color);
+GstyleColor *gstyle_color_new (const gchar *name,
+ GstyleColorKind kind,
+ guint red,
+ guint green,
+ guint blue,
+ guint alpha);
+GstyleColor *gstyle_color_new_from_rgba (const gchar *name,
+ GstyleColorKind kind,
+ GdkRGBA *rgba);
+GstyleColor *gstyle_color_new_from_hsla (const gchar *name,
+ GstyleColorKind kind,
+ gdouble hue,
+ gdouble saturation,
+ gdouble lightness,
+ gdouble alpha);
+GstyleColor *gstyle_color_new_from_string (const gchar *name,
+ const gchar *color_string);
+
+/* TODO: add an async functions */
+GPtrArray *gstyle_color_fuzzy_parse_color_string (const gchar *color_string);
+GPtrArray *gstyle_color_parse (const gchar *string);
+gboolean gstyle_color_parse_color_string (const gchar *color_string,
+ GdkRGBA *rgba,
+ GstyleColorKind *kind);
+void gstyle_color_to_hsla (GstyleColor *self,
+ gdouble *hue,
+ gdouble *saturation,
+ gdouble *lightness,
+ gdouble *alpha);
+gchar *gstyle_color_to_string (GstyleColor *self,
+ GstyleColorKind kind);
+GstyleColorKind gstyle_color_get_kind (GstyleColor *self);
+const gchar *gstyle_color_get_name (GstyleColor *self);
+GdkRGBA *gstyle_color_get_rgba (GstyleColor *self);
+void gstyle_color_set_kind (GstyleColor *self,
+ GstyleColorKind kind);
+void gstyle_color_set_name (GstyleColor *self,
+ const gchar *name);
+void gstyle_color_set_rgba (GstyleColor *self,
+ GdkRGBA *rgba);
+void gstyle_color_set_alpha (GstyleColor *self,
+ gdouble alpha);
+void gstyle_color_fill_rgba (GstyleColor *self,
+ GdkRGBA *rgba);
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLOR_H */
diff --git a/contrib/gstyle/gstyle-colorlexer.c b/contrib/gstyle/gstyle-colorlexer.c
new file mode 100644
index 0000000..e1db50b
--- /dev/null
+++ b/contrib/gstyle/gstyle-colorlexer.c
@@ -0,0 +1,3002 @@
+/* Generated by re2c 0.16 on Wed Jun 29 18:01:24 2016 */
+/* gstyle-colorlexer.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-colorlexer"
+
+#include <glib.h>
+
+#include "gstyle-color.h"
+#include "gstyle-color-item.h"
+#include "gstyle-types.h"
+#include "gstyle-utils.h"
+
+#include "gstyle-colorlexer.h"
+
+#define PARSE_ARRAY_INITIAL_SIZE 32
+
+#define YYCTYPE guchar
+#define YYCURSOR cursor
+#define YYMARKER s->ptr
+#define YYCTXMARKER s->ptr_ctx
+
+static void
+gstyle_colorlexer_init (GstyleColorScanner *s,
+ const gchar *data)
+{
+ s->start = data;
+ s->cursor = data;
+}
+
+static gint
+gstyle_colorlexer_scan (GstyleColorScanner *s)
+{
+ const gchar *cursor = s->cursor;
+
+standard:
+ s->start = cursor;
+
+
+ {
+ guchar yych;
+ static const unsigned char yybm[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 128, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 128, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+ yych = *YYCURSOR;
+ if (yych <= '.') {
+ if (yych <= '!') {
+ if (yych <= '\t') {
+ if (yych <= 0x00) goto yy3;
+ if (yych <= 0x08) goto yy5;
+ goto yy7;
+ } else {
+ if (yych == ' ') goto yy7;
+ goto yy5;
+ }
+ } else {
+ if (yych <= '(') {
+ if (yych <= '"') goto yy7;
+ if (yych <= '&') goto yy5;
+ goto yy7;
+ } else {
+ if (yych == ',') goto yy7;
+ goto yy5;
+ }
+ }
+ } else {
+ if (yych <= 0xDF) {
+ if (yych <= ':') {
+ if (yych <= '/') goto yy10;
+ if (yych <= '9') goto yy5;
+ goto yy7;
+ } else {
+ if (yych <= 0x7F) goto yy5;
+ if (yych >= 0xC2) goto yy11;
+ }
+ } else {
+ if (yych <= 0xF0) {
+ if (yych <= 0xE0) goto yy12;
+ if (yych <= 0xEF) goto yy13;
+ goto yy14;
+ } else {
+ if (yych <= 0xF3) goto yy15;
+ if (yych <= 0xF4) goto yy16;
+ }
+ }
+ }
+yy2:
+yy3:
+ ++YYCURSOR;
+ {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_EOF;}
+yy5:
+ ++YYCURSOR;
+yy6:
+ { goto standard; }
+yy7:
+ ++YYCURSOR;
+ yych = *YYCURSOR;
+ if (yybm[0+yych] & 128) {
+ goto yy7;
+ }
+ { s->start = cursor;goto color; }
+yy10:
+ yych = *++YYCURSOR;
+ if (yych == '*') goto yy17;
+ goto yy6;
+yy11:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy2;
+ if (yych <= 0xBF) goto yy5;
+ goto yy2;
+yy12:
+ yych = *++YYCURSOR;
+ if (yych <= 0x9F) goto yy2;
+ if (yych <= 0xBF) goto yy11;
+ goto yy2;
+yy13:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy2;
+ if (yych <= 0xBF) goto yy11;
+ goto yy2;
+yy14:
+ yych = *++YYCURSOR;
+ if (yych <= 0x8F) goto yy2;
+ if (yych <= 0xBF) goto yy13;
+ goto yy2;
+yy15:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy2;
+ if (yych <= 0xBF) goto yy13;
+ goto yy2;
+yy16:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy2;
+ if (yych <= 0x8F) goto yy13;
+ goto yy2;
+yy17:
+ ++YYCURSOR;
+ { goto comment; }
+ }
+
+
+color:
+
+ {
+ guchar yych;
+ unsigned int yyaccept = 0;
+ static const unsigned char yybm[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 128, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 128, 0, 0, 0, 0, 128, 0, 0,
+ 0, 0, 0, 0, 128, 0, 128, 0,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+ yych = *YYCURSOR;
+ if (yych <= 'o') {
+ if (yych <= 'e') {
+ if (yych <= '`') {
+ if (yych <= 0x00) goto yy22;
+ if (yych == '#') goto yy26;
+ goto yy24;
+ } else {
+ if (yych <= 'b') {
+ if (yych <= 'a') goto yy27;
+ goto yy28;
+ } else {
+ if (yych <= 'c') goto yy29;
+ if (yych <= 'd') goto yy30;
+ goto yy24;
+ }
+ }
+ } else {
+ if (yych <= 'j') {
+ if (yych <= 'g') {
+ if (yych <= 'f') goto yy31;
+ goto yy32;
+ } else {
+ if (yych <= 'h') goto yy33;
+ if (yych <= 'i') goto yy34;
+ goto yy24;
+ }
+ } else {
+ if (yych <= 'l') {
+ if (yych <= 'k') goto yy35;
+ goto yy36;
+ } else {
+ if (yych <= 'm') goto yy37;
+ if (yych <= 'n') goto yy38;
+ goto yy39;
+ }
+ }
+ }
+ } else {
+ if (yych <= 'x') {
+ if (yych <= 's') {
+ if (yych <= 'p') goto yy40;
+ if (yych <= 'q') goto yy24;
+ if (yych <= 'r') goto yy41;
+ goto yy42;
+ } else {
+ if (yych <= 'u') {
+ if (yych <= 't') goto yy43;
+ goto yy24;
+ } else {
+ if (yych <= 'v') goto yy44;
+ if (yych <= 'w') goto yy45;
+ goto yy24;
+ }
+ }
+ } else {
+ if (yych <= 0xE0) {
+ if (yych <= 0x7F) {
+ if (yych <= 'y') goto yy46;
+ goto yy24;
+ } else {
+ if (yych <= 0xC1) goto yy21;
+ if (yych <= 0xDF) goto yy47;
+ goto yy48;
+ }
+ } else {
+ if (yych <= 0xF0) {
+ if (yych <= 0xEF) goto yy49;
+ goto yy50;
+ } else {
+ if (yych <= 0xF3) goto yy51;
+ if (yych <= 0xF4) goto yy52;
+ }
+ }
+ }
+ }
+yy21:
+ YYCURSOR = YYMARKER;
+ if (yyaccept == 0) {
+ goto yy25;
+ } else {
+ goto yy229;
+ }
+yy22:
+ ++YYCURSOR;
+ {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_EOF;}
+yy24:
+ ++YYCURSOR;
+yy25:
+ { goto standard; }
+yy26:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'e': goto yy53;
+ case 'a': goto yy54;
+ case 'b': goto yy55;
+ case 'c': goto yy56;
+ case 'd': goto yy57;
+ case 'f': goto yy58;
+ case 'g': goto yy59;
+ case 'h': goto yy60;
+ case 'i': goto yy61;
+ case 'k': goto yy62;
+ case 'l': goto yy63;
+ case 'm': goto yy64;
+ case 'n': goto yy65;
+ case 'o': goto yy66;
+ case 'p': goto yy67;
+ case 'r': goto yy68;
+ case 's': goto yy69;
+ case 't': goto yy70;
+ case 'v': goto yy71;
+ case 'w': goto yy72;
+ case 'y': goto yy73;
+ default: goto yy25;
+ }
+yy27:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'l': goto yy74;
+ case 'n': goto yy75;
+ case 'q': goto yy76;
+ case 'z': goto yy77;
+ default: goto yy25;
+ }
+yy28:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'e': goto yy78;
+ case 'i': goto yy79;
+ case 'l': goto yy80;
+ case 'r': goto yy81;
+ case 'u': goto yy82;
+ default: goto yy25;
+ }
+yy29:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'a': goto yy83;
+ case 'h': goto yy84;
+ case 'o': goto yy85;
+ case 'r': goto yy86;
+ case 'y': goto yy87;
+ default: goto yy25;
+ }
+yy30:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'a': goto yy88;
+ case 'e': goto yy89;
+ case 'i': goto yy90;
+ case 'o': goto yy91;
+ default: goto yy25;
+ }
+yy31:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'i': goto yy92;
+ case 'l': goto yy93;
+ case 'o': goto yy94;
+ case 'u': goto yy95;
+ default: goto yy25;
+ }
+yy32:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'a': goto yy96;
+ case 'h': goto yy97;
+ case 'o': goto yy98;
+ case 'r': goto yy99;
+ default: goto yy25;
+ }
+yy33:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'o') goto yy100;
+ if (yych == 's') goto yy101;
+ goto yy25;
+yy34:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'n') goto yy102;
+ if (yych == 'v') goto yy103;
+ goto yy25;
+yy35:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'h') goto yy104;
+ goto yy25;
+yy36:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= 'd') {
+ if (yych == 'a') goto yy105;
+ goto yy25;
+ } else {
+ if (yych <= 'e') goto yy106;
+ if (yych == 'i') goto yy107;
+ goto yy25;
+ }
+yy37:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'a': goto yy108;
+ case 'e': goto yy109;
+ case 'i': goto yy110;
+ case 'o': goto yy111;
+ default: goto yy25;
+ }
+yy38:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'a') goto yy112;
+ goto yy25;
+yy39:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'l') goto yy113;
+ if (yych == 'r') goto yy114;
+ goto yy25;
+yy40:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'a': goto yy115;
+ case 'e': goto yy116;
+ case 'i': goto yy117;
+ case 'l': goto yy118;
+ case 'o': goto yy119;
+ case 'u': goto yy120;
+ default: goto yy25;
+ }
+yy41:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= 'f') {
+ if (yych == 'e') goto yy121;
+ goto yy25;
+ } else {
+ if (yych <= 'g') goto yy122;
+ if (yych == 'o') goto yy123;
+ goto yy25;
+ }
+yy42:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'a': goto yy124;
+ case 'e': goto yy125;
+ case 'i': goto yy126;
+ case 'k': goto yy127;
+ case 'l': goto yy128;
+ case 'n': goto yy129;
+ case 'p': goto yy130;
+ case 't': goto yy131;
+ default: goto yy25;
+ }
+yy43:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch (yych) {
+ case 'a': goto yy132;
+ case 'e': goto yy133;
+ case 'h': goto yy134;
+ case 'o': goto yy135;
+ case 'u': goto yy136;
+ default: goto yy25;
+ }
+yy44:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'i') goto yy137;
+ goto yy25;
+yy45:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'h') goto yy138;
+ goto yy25;
+yy46:
+ yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych == 'e') goto yy139;
+ goto yy25;
+yy47:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy21;
+ if (yych <= 0xBF) goto yy24;
+ goto yy21;
+yy48:
+ yych = *++YYCURSOR;
+ if (yych <= 0x9F) goto yy21;
+ if (yych <= 0xBF) goto yy47;
+ goto yy21;
+yy49:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy21;
+ if (yych <= 0xBF) goto yy47;
+ goto yy21;
+yy50:
+ yych = *++YYCURSOR;
+ if (yych <= 0x8F) goto yy21;
+ if (yych <= 0xBF) goto yy49;
+ goto yy21;
+yy51:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy21;
+ if (yych <= 0xBF) goto yy49;
+ goto yy21;
+yy52:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy21;
+ if (yych <= 0x8F) goto yy49;
+ goto yy21;
+yy53:
+ yych = *++YYCURSOR;
+ if (yych <= '@') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy140;
+ goto yy21;
+ } else {
+ if (yych <= 'F') goto yy140;
+ if (yych <= '`') goto yy21;
+ if (yych <= 'f') goto yy140;
+ goto yy21;
+ }
+yy54:
+ yych = *++YYCURSOR;
+ if (yych <= 'k') {
+ if (yych <= '@') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy140;
+ goto yy21;
+ } else {
+ if (yych <= 'F') goto yy140;
+ if (yych <= '`') goto yy21;
+ if (yych <= 'f') goto yy140;
+ goto yy21;
+ }
+ } else {
+ if (yych <= 'p') {
+ if (yych <= 'l') goto yy74;
+ if (yych == 'n') goto yy75;
+ goto yy21;
+ } else {
+ if (yych <= 'q') goto yy76;
+ if (yych == 'z') goto yy77;
+ goto yy21;
+ }
+ }
+yy55:
+ yych = *++YYCURSOR;
+ if (yych <= 'f') {
+ if (yych <= 'F') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy140;
+ if (yych <= '@') goto yy21;
+ goto yy140;
+ } else {
+ if (yych <= '`') goto yy21;
+ if (yych == 'e') goto yy141;
+ goto yy140;
+ }
+ } else {
+ if (yych <= 'l') {
+ if (yych == 'i') goto yy79;
+ if (yych <= 'k') goto yy21;
+ goto yy80;
+ } else {
+ if (yych <= 'r') {
+ if (yych <= 'q') goto yy21;
+ goto yy81;
+ } else {
+ if (yych == 'u') goto yy82;
+ goto yy21;
+ }
+ }
+ }
+yy56:
+ yych = *++YYCURSOR;
+ if (yych <= 'g') {
+ if (yych <= 'F') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy140;
+ if (yych <= '@') goto yy21;
+ goto yy140;
+ } else {
+ if (yych <= '`') goto yy21;
+ if (yych <= 'a') goto yy142;
+ if (yych <= 'f') goto yy140;
+ goto yy21;
+ }
+ } else {
+ if (yych <= 'q') {
+ if (yych <= 'h') goto yy84;
+ if (yych == 'o') goto yy85;
+ goto yy21;
+ } else {
+ if (yych <= 'r') goto yy86;
+ if (yych == 'y') goto yy87;
+ goto yy21;
+ }
+ }
+yy57:
+ yych = *++YYCURSOR;
+ if (yych <= 'd') {
+ if (yych <= '@') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy140;
+ goto yy21;
+ } else {
+ if (yych <= 'F') goto yy140;
+ if (yych <= '`') goto yy21;
+ if (yych <= 'a') goto yy143;
+ goto yy140;
+ }
+ } else {
+ if (yych <= 'h') {
+ if (yych <= 'e') goto yy144;
+ if (yych <= 'f') goto yy140;
+ goto yy21;
+ } else {
+ if (yych <= 'i') goto yy90;
+ if (yych == 'o') goto yy91;
+ goto yy21;
+ }
+ }
+yy58:
+ yych = *++YYCURSOR;
+ if (yych <= 'h') {
+ if (yych <= '@') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy140;
+ goto yy21;
+ } else {
+ if (yych <= 'F') goto yy140;
+ if (yych <= '`') goto yy21;
+ if (yych <= 'f') goto yy140;
+ goto yy21;
+ }
+ } else {
+ if (yych <= 'n') {
+ if (yych <= 'i') goto yy92;
+ if (yych == 'l') goto yy93;
+ goto yy21;
+ } else {
+ if (yych <= 'o') goto yy94;
+ if (yych == 'u') goto yy95;
+ goto yy21;
+ }
+ }
+yy59:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case 'a': goto yy96;
+ case 'h': goto yy97;
+ case 'o': goto yy98;
+ case 'r': goto yy99;
+ default: goto yy21;
+ }
+yy60:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy100;
+ if (yych == 's') goto yy101;
+ goto yy21;
+yy61:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy102;
+ if (yych == 'v') goto yy103;
+ goto yy21;
+yy62:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy104;
+ goto yy21;
+yy63:
+ yych = *++YYCURSOR;
+ if (yych <= 'd') {
+ if (yych == 'a') goto yy105;
+ goto yy21;
+ } else {
+ if (yych <= 'e') goto yy106;
+ if (yych == 'i') goto yy107;
+ goto yy21;
+ }
+yy64:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case 'a': goto yy108;
+ case 'e': goto yy109;
+ case 'i': goto yy110;
+ case 'o': goto yy111;
+ default: goto yy21;
+ }
+yy65:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy112;
+ goto yy21;
+yy66:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy113;
+ if (yych == 'r') goto yy114;
+ goto yy21;
+yy67:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case 'a': goto yy115;
+ case 'e': goto yy116;
+ case 'i': goto yy117;
+ case 'l': goto yy118;
+ case 'o': goto yy119;
+ case 'u': goto yy120;
+ default: goto yy21;
+ }
+yy68:
+ yych = *++YYCURSOR;
+ if (yych <= 'f') {
+ if (yych == 'e') goto yy121;
+ goto yy21;
+ } else {
+ if (yych <= 'g') goto yy122;
+ if (yych == 'o') goto yy123;
+ goto yy21;
+ }
+yy69:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case 'a': goto yy124;
+ case 'e': goto yy125;
+ case 'i': goto yy126;
+ case 'k': goto yy127;
+ case 'l': goto yy128;
+ case 'n': goto yy129;
+ case 'p': goto yy130;
+ case 't': goto yy131;
+ default: goto yy21;
+ }
+yy70:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case 'a': goto yy132;
+ case 'e': goto yy133;
+ case 'h': goto yy134;
+ case 'o': goto yy135;
+ case 'u': goto yy136;
+ default: goto yy21;
+ }
+yy71:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy137;
+ goto yy21;
+yy72:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy138;
+ goto yy21;
+yy73:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy139;
+ goto yy21;
+yy74:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy145;
+ goto yy21;
+yy75:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy146;
+ goto yy21;
+yy76:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy147;
+ goto yy21;
+yy77:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy148;
+ goto yy21;
+yy78:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy149;
+ goto yy21;
+yy79:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy150;
+ goto yy21;
+yy80:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy151;
+ if (yych == 'u') goto yy152;
+ goto yy21;
+yy81:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy153;
+ goto yy21;
+yy82:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy154;
+ goto yy21;
+yy83:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy155;
+ goto yy21;
+yy84:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy156;
+ if (yych == 'o') goto yy157;
+ goto yy21;
+yy85:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy158;
+ goto yy21;
+yy86:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy159;
+ goto yy21;
+yy87:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy132;
+ goto yy21;
+yy88:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy160;
+ goto yy21;
+yy89:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy161;
+ goto yy21;
+yy90:
+ yych = *++YYCURSOR;
+ if (yych == 'm') goto yy162;
+ goto yy21;
+yy91:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy163;
+ goto yy21;
+yy92:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy164;
+ goto yy21;
+yy93:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy165;
+ goto yy21;
+yy94:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy166;
+ goto yy21;
+yy95:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy167;
+ goto yy21;
+yy96:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy168;
+ goto yy21;
+yy97:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy169;
+ goto yy21;
+yy98:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy170;
+ goto yy21;
+yy99:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy171;
+ if (yych == 'e') goto yy172;
+ goto yy21;
+yy100:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy173;
+ if (yych == 't') goto yy174;
+ goto yy21;
+yy101:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy175;
+ goto yy21;
+yy102:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy176;
+ goto yy21;
+yy103:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy177;
+ goto yy21;
+yy104:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy178;
+ goto yy21;
+yy105:
+ yych = *++YYCURSOR;
+ if (yych <= 'u') goto yy21;
+ if (yych <= 'v') goto yy179;
+ if (yych <= 'w') goto yy180;
+ goto yy21;
+yy106:
+ yych = *++YYCURSOR;
+ if (yych == 'm') goto yy181;
+ goto yy21;
+yy107:
+ yych = *++YYCURSOR;
+ if (yych <= 'l') {
+ if (yych == 'g') goto yy182;
+ goto yy21;
+ } else {
+ if (yych <= 'm') goto yy183;
+ if (yych <= 'n') goto yy184;
+ goto yy21;
+ }
+yy108:
+ yych = *++YYCURSOR;
+ if (yych == 'g') goto yy185;
+ if (yych == 'r') goto yy186;
+ goto yy21;
+yy109:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy187;
+ goto yy21;
+yy110:
+ yych = *++YYCURSOR;
+ if (yych <= 'm') {
+ if (yych == 'd') goto yy188;
+ goto yy21;
+ } else {
+ if (yych <= 'n') goto yy189;
+ if (yych == 's') goto yy190;
+ goto yy21;
+ }
+yy111:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy191;
+ goto yy21;
+yy112:
+ yych = *++YYCURSOR;
+ if (yych == 'v') goto yy192;
+ goto yy21;
+yy113:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy193;
+ if (yych == 'i') goto yy194;
+ goto yy21;
+yy114:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy195;
+ if (yych == 'c') goto yy196;
+ goto yy21;
+yy115:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy197;
+ if (yych == 'p') goto yy198;
+ goto yy21;
+yy116:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy199;
+ if (yych == 'r') goto yy200;
+ goto yy21;
+yy117:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy201;
+ goto yy21;
+yy118:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy202;
+ goto yy21;
+yy119:
+ yych = *++YYCURSOR;
+ if (yych == 'w') goto yy203;
+ goto yy21;
+yy120:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy204;
+ goto yy21;
+yy121:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy205;
+ goto yy21;
+yy122:
+ yych = *++YYCURSOR;
+ if (yych == 'b') goto yy206;
+ goto yy21;
+yy123:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy207;
+ if (yych == 'y') goto yy208;
+ goto yy21;
+yy124:
+ yych = *++YYCURSOR;
+ if (yych <= 'k') {
+ if (yych == 'd') goto yy209;
+ goto yy21;
+ } else {
+ if (yych <= 'l') goto yy210;
+ if (yych == 'n') goto yy211;
+ goto yy21;
+ }
+yy125:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy212;
+ goto yy21;
+yy126:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy213;
+ if (yych == 'l') goto yy214;
+ goto yy21;
+yy127:
+ yych = *++YYCURSOR;
+ if (yych == 'y') goto yy215;
+ goto yy21;
+yy128:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy216;
+ goto yy21;
+yy129:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy217;
+ goto yy21;
+yy130:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy218;
+ goto yy21;
+yy131:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy219;
+ goto yy21;
+yy132:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy205;
+ goto yy21;
+yy133:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy220;
+ goto yy21;
+yy134:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy221;
+ goto yy21;
+yy135:
+ yych = *++YYCURSOR;
+ if (yych == 'm') goto yy222;
+ goto yy21;
+yy136:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy223;
+ goto yy21;
+yy137:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy224;
+ goto yy21;
+yy138:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy225;
+ if (yych == 'i') goto yy226;
+ goto yy21;
+yy139:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy227;
+ goto yy21;
+yy140:
+ yych = *++YYCURSOR;
+ if (yych <= '@') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy228;
+ goto yy21;
+ } else {
+ if (yych <= 'F') goto yy228;
+ if (yych <= '`') goto yy21;
+ if (yych <= 'f') goto yy228;
+ goto yy21;
+ }
+yy141:
+ yych = *++YYCURSOR;
+ if (yych <= 'F') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy228;
+ if (yych <= '@') goto yy21;
+ goto yy228;
+ } else {
+ if (yych <= 'f') {
+ if (yych <= '`') goto yy21;
+ goto yy228;
+ } else {
+ if (yych == 'i') goto yy149;
+ goto yy21;
+ }
+ }
+yy142:
+ yych = *++YYCURSOR;
+ if (yych <= 'F') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy228;
+ if (yych <= '@') goto yy21;
+ goto yy228;
+ } else {
+ if (yych <= 'c') {
+ if (yych <= '`') goto yy21;
+ goto yy228;
+ } else {
+ if (yych <= 'd') goto yy230;
+ if (yych <= 'f') goto yy228;
+ goto yy21;
+ }
+ }
+yy143:
+ yych = *++YYCURSOR;
+ if (yych <= 'F') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy228;
+ if (yych <= '@') goto yy21;
+ goto yy228;
+ } else {
+ if (yych <= 'f') {
+ if (yych <= '`') goto yy21;
+ goto yy228;
+ } else {
+ if (yych == 'r') goto yy160;
+ goto yy21;
+ }
+ }
+yy144:
+ yych = *++YYCURSOR;
+ if (yych <= 'F') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy228;
+ if (yych <= '@') goto yy21;
+ goto yy228;
+ } else {
+ if (yych <= 'd') {
+ if (yych <= '`') goto yy21;
+ goto yy228;
+ } else {
+ if (yych <= 'e') goto yy231;
+ if (yych <= 'f') goto yy228;
+ goto yy21;
+ }
+ }
+yy145:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy232;
+ goto yy21;
+yy146:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy233;
+ goto yy21;
+yy147:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy234;
+ goto yy21;
+yy148:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy235;
+ goto yy21;
+yy149:
+ yych = *++YYCURSOR;
+ if (yych == 'g') goto yy235;
+ goto yy21;
+yy150:
+ yych = *++YYCURSOR;
+ if (yych == 'q') goto yy236;
+ goto yy21;
+yy151:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy201;
+ if (yych == 'n') goto yy237;
+ goto yy21;
+yy152:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy238;
+ goto yy21;
+yy153:
+ yych = *++YYCURSOR;
+ if (yych == 'w') goto yy132;
+ goto yy21;
+yy154:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy239;
+ goto yy21;
+yy155:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy240;
+ goto yy21;
+yy156:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy241;
+ goto yy21;
+yy157:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy242;
+ goto yy21;
+yy158:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy220;
+ if (yych == 'n') goto yy243;
+ goto yy21;
+yy159:
+ yych = *++YYCURSOR;
+ if (yych == 'm') goto yy244;
+ goto yy21;
+yy160:
+ yych = *++YYCURSOR;
+ if (yych == 'k') goto yy245;
+ goto yy21;
+yy161:
+ yych = *++YYCURSOR;
+ if (yych == 'p') goto yy246;
+ goto yy21;
+yy162:
+ yych = *++YYCURSOR;
+ if (yych == 'g') goto yy247;
+ goto yy21;
+yy163:
+ yych = *++YYCURSOR;
+ if (yych == 'g') goto yy248;
+ goto yy21;
+yy164:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy249;
+ goto yy21;
+yy165:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy250;
+ goto yy21;
+yy166:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy251;
+ goto yy21;
+yy167:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy252;
+ goto yy21;
+yy168:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy253;
+ goto yy21;
+yy169:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy254;
+ goto yy21;
+yy170:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy255;
+ goto yy21;
+yy171:
+ yych = *++YYCURSOR;
+ if (yych == 'y') goto yy205;
+ goto yy21;
+yy172:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy256;
+ if (yych == 'y') goto yy205;
+ goto yy21;
+yy173:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy257;
+ goto yy21;
+yy174:
+ yych = *++YYCURSOR;
+ if (yych == 'p') goto yy258;
+ goto yy21;
+yy175:
+ yych = *++YYCURSOR;
+ if (yych == '(') goto yy259;
+ if (yych == 'a') goto yy260;
+ goto yy21;
+yy176:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy261;
+ goto yy21;
+yy177:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy171;
+ goto yy21;
+yy178:
+ yych = *++YYCURSOR;
+ if (yych == 'k') goto yy262;
+ goto yy21;
+yy179:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy263;
+ goto yy21;
+yy180:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy264;
+ goto yy21;
+yy181:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy265;
+ goto yy21;
+yy182:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy266;
+ goto yy21;
+yy183:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy267;
+ goto yy21;
+yy184:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy132;
+ goto yy21;
+yy185:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy268;
+ goto yy21;
+yy186:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy269;
+ goto yy21;
+yy187:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy270;
+ goto yy21;
+yy188:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy271;
+ goto yy21;
+yy189:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy272;
+ goto yy21;
+yy190:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy273;
+ goto yy21;
+yy191:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy274;
+ goto yy21;
+yy192:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy275;
+ if (yych == 'y') goto yy205;
+ goto yy21;
+yy193:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy276;
+ goto yy21;
+yy194:
+ yych = *++YYCURSOR;
+ if (yych == 'v') goto yy277;
+ goto yy21;
+yy195:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy278;
+ goto yy21;
+yy196:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy279;
+ goto yy21;
+yy197:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy280;
+ goto yy21;
+yy198:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy281;
+ goto yy21;
+yy199:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy282;
+ goto yy21;
+yy200:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy205;
+ goto yy21;
+yy201:
+ yych = *++YYCURSOR;
+ if (yych == 'k') goto yy205;
+ goto yy21;
+yy202:
+ yych = *++YYCURSOR;
+ if (yych == 'm') goto yy205;
+ goto yy21;
+yy203:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy248;
+ goto yy21;
+yy204:
+ yych = *++YYCURSOR;
+ if (yych == 'p') goto yy283;
+ goto yy21;
+yy205:
+ yych = *++YYCURSOR;
+ YYCTXMARKER = YYCURSOR;
+ if (yych <= '&') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy284;
+ goto yy21;
+ } else {
+ if (yych == '!') goto yy21;
+ if (yych <= '"') goto yy284;
+ goto yy21;
+ }
+ } else {
+ if (yych <= '+') {
+ if (yych == '(') goto yy21;
+ if (yych <= ')') goto yy284;
+ goto yy21;
+ } else {
+ if (yych <= ',') goto yy284;
+ if (yych == ';') goto yy284;
+ goto yy21;
+ }
+ }
+yy206:
+ yych = *++YYCURSOR;
+ if (yych == '(') goto yy286;
+ if (yych == 'a') goto yy287;
+ goto yy21;
+yy207:
+ yych = *++YYCURSOR;
+ if (yych == 'y') goto yy288;
+ goto yy21;
+yy208:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy289;
+ goto yy21;
+yy209:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy290;
+ goto yy21;
+yy210:
+ yych = *++YYCURSOR;
+ if (yych == 'm') goto yy269;
+ goto yy21;
+yy211:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy207;
+ goto yy21;
+yy212:
+ yych = *++YYCURSOR;
+ if (yych == 'g') goto yy291;
+ if (yych == 's') goto yy292;
+ goto yy21;
+yy213:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy293;
+ goto yy21;
+yy214:
+ yych = *++YYCURSOR;
+ if (yych == 'v') goto yy294;
+ goto yy21;
+yy215:
+ yych = *++YYCURSOR;
+ if (yych == 'b') goto yy295;
+ goto yy21;
+yy216:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy296;
+ goto yy21;
+yy217:
+ yych = *++YYCURSOR;
+ if (yych == 'w') goto yy205;
+ goto yy21;
+yy218:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy297;
+ goto yy21;
+yy219:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy289;
+ goto yy21;
+yy220:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy205;
+ goto yy21;
+yy221:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy298;
+ goto yy21;
+yy222:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy299;
+ goto yy21;
+yy223:
+ yych = *++YYCURSOR;
+ if (yych == 'q') goto yy300;
+ goto yy21;
+yy224:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy301;
+ goto yy21;
+yy225:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy302;
+ goto yy21;
+yy226:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy303;
+ goto yy21;
+yy227:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy304;
+ goto yy21;
+yy228:
+ yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= '@') {
+ if (yych <= '/') goto yy229;
+ if (yych <= '9') goto yy305;
+ } else {
+ if (yych <= 'F') goto yy305;
+ if (yych <= '`') goto yy229;
+ if (yych <= 'f') goto yy305;
+ }
+yy229:
+ {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_HEX;}
+yy230:
+ yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= 'F') {
+ if (yych <= '/') goto yy229;
+ if (yych <= '9') goto yy305;
+ if (yych <= '@') goto yy229;
+ goto yy305;
+ } else {
+ if (yych <= 'd') {
+ if (yych <= '`') goto yy229;
+ goto yy305;
+ } else {
+ if (yych <= 'e') goto yy306;
+ if (yych <= 'f') goto yy305;
+ goto yy229;
+ }
+ }
+yy231:
+ yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ if (yych <= 'F') {
+ if (yych <= '/') goto yy229;
+ if (yych <= '9') goto yy305;
+ if (yych <= '@') goto yy229;
+ goto yy305;
+ } else {
+ if (yych <= 'f') {
+ if (yych <= '`') goto yy229;
+ goto yy305;
+ } else {
+ if (yych == 'p') goto yy246;
+ goto yy229;
+ }
+ }
+yy232:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy215;
+ goto yy21;
+yy233:
+ yych = *++YYCURSOR;
+ if (yych == 'q') goto yy307;
+ goto yy21;
+yy234:
+ yych = *++YYCURSOR;
+ YYCTXMARKER = YYCURSOR;
+ if (yych <= '\'') {
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy284;
+ if (yych <= 0x1F) goto yy21;
+ goto yy284;
+ } else {
+ if (yych == '"') goto yy284;
+ if (yych <= '&') goto yy21;
+ goto yy284;
+ }
+ } else {
+ if (yych <= ',') {
+ if (yych == ')') goto yy284;
+ if (yych <= '+') goto yy21;
+ goto yy284;
+ } else {
+ if (yych <= ';') {
+ if (yych <= ':') goto yy21;
+ goto yy284;
+ } else {
+ if (yych == 'm') goto yy308;
+ goto yy21;
+ }
+ }
+ }
+yy235:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy205;
+ goto yy21;
+yy236:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy235;
+ goto yy21;
+yy237:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy309;
+ goto yy21;
+yy238:
+ yych = *++YYCURSOR;
+ YYCTXMARKER = YYCURSOR;
+ if (yych <= '\'') {
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy284;
+ if (yych <= 0x1F) goto yy21;
+ goto yy284;
+ } else {
+ if (yych == '"') goto yy284;
+ if (yych <= '&') goto yy21;
+ goto yy284;
+ }
+ } else {
+ if (yych <= ',') {
+ if (yych == ')') goto yy284;
+ if (yych <= '+') goto yy21;
+ goto yy284;
+ } else {
+ if (yych <= ';') {
+ if (yych <= ':') goto yy21;
+ goto yy284;
+ } else {
+ if (yych == 'v') goto yy71;
+ goto yy21;
+ }
+ }
+ }
+yy239:
+ yych = *++YYCURSOR;
+ if (yych == 'y') goto yy310;
+ goto yy21;
+yy240:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy215;
+ goto yy21;
+yy241:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy311;
+ goto yy21;
+yy242:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy312;
+ goto yy21;
+yy243:
+ yych = *++YYCURSOR;
+ if (yych == 'f') goto yy313;
+ if (yych == 's') goto yy314;
+ goto yy21;
+yy244:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy269;
+ goto yy21;
+yy245:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case 'b': goto yy295;
+ case 'c': goto yy315;
+ case 'g': goto yy316;
+ case 'k': goto yy62;
+ case 'm': goto yy317;
+ case 'o': goto yy318;
+ case 'r': goto yy319;
+ case 's': goto yy320;
+ case 't': goto yy321;
+ case 'v': goto yy71;
+ default: goto yy21;
+ }
+yy246:
+ yych = *++YYCURSOR;
+ if (yych == 'p') goto yy258;
+ if (yych == 's') goto yy322;
+ goto yy21;
+yy247:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy323;
+ goto yy21;
+yy248:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy324;
+ goto yy21;
+yy249:
+ yych = *++YYCURSOR;
+ if (yych == 'b') goto yy325;
+ goto yy21;
+yy250:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy326;
+ goto yy21;
+yy251:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy327;
+ goto yy21;
+yy252:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy328;
+ goto yy21;
+yy253:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy329;
+ goto yy21;
+yy254:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy330;
+ goto yy21;
+yy255:
+ yych = *++YYCURSOR;
+ YYCTXMARKER = YYCURSOR;
+ if (yych <= '\'') {
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy284;
+ if (yych <= 0x1F) goto yy21;
+ goto yy284;
+ } else {
+ if (yych == '"') goto yy284;
+ if (yych <= '&') goto yy21;
+ goto yy284;
+ }
+ } else {
+ if (yych <= ',') {
+ if (yych == ')') goto yy284;
+ if (yych <= '+') goto yy21;
+ goto yy284;
+ } else {
+ if (yych <= ';') {
+ if (yych <= ':') goto yy21;
+ goto yy284;
+ } else {
+ if (yych == 'e') goto yy331;
+ goto yy21;
+ }
+ }
+ }
+yy256:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy332;
+ goto yy21;
+yy257:
+ yych = *++YYCURSOR;
+ if (yych == 'y') goto yy333;
+ goto yy21;
+yy258:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy117;
+ goto yy21;
+yy259:
+ yych = *++YYCURSOR;
+ if (yych == ')') goto yy21;
+ goto yy335;
+yy260:
+ yych = *++YYCURSOR;
+ if (yych == '(') goto yy259;
+ goto yy21;
+yy261:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy336;
+ if (yych == 'g') goto yy337;
+ goto yy21;
+yy262:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy205;
+ goto yy21;
+yy263:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy338;
+ goto yy21;
+yy264:
+ yych = *++YYCURSOR;
+ if (yych == 'g') goto yy291;
+ goto yy21;
+yy265:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy339;
+ goto yy21;
+yy266:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy340;
+ goto yy21;
+yy267:
+ yych = *++YYCURSOR;
+ YYCTXMARKER = YYCURSOR;
+ if (yych <= '\'') {
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy284;
+ if (yych <= 0x1F) goto yy21;
+ goto yy284;
+ } else {
+ if (yych == '"') goto yy284;
+ if (yych <= '&') goto yy21;
+ goto yy284;
+ }
+ } else {
+ if (yych <= ',') {
+ if (yych == ')') goto yy284;
+ if (yych <= '+') goto yy21;
+ goto yy284;
+ } else {
+ if (yych <= ';') {
+ if (yych <= ':') goto yy21;
+ goto yy284;
+ } else {
+ if (yych == 'g') goto yy291;
+ goto yy21;
+ }
+ }
+ }
+yy268:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy341;
+ goto yy21;
+yy269:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy132;
+ goto yy21;
+yy270:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy342;
+ goto yy21;
+yy271:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy343;
+ goto yy21;
+yy272:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy344;
+ goto yy21;
+yy273:
+ yych = *++YYCURSOR;
+ if (yych == 'y') goto yy345;
+ goto yy21;
+yy274:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy346;
+ goto yy21;
+yy275:
+ yych = *++YYCURSOR;
+ if (yych == 'j') goto yy347;
+ goto yy21;
+yy276:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy348;
+ goto yy21;
+yy277:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy349;
+ goto yy21;
+yy278:
+ yych = *++YYCURSOR;
+ if (yych == 'g') goto yy350;
+ goto yy21;
+yy279:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy121;
+ goto yy21;
+yy280:
+ yych = *++YYCURSOR;
+ if (yych <= 's') {
+ if (yych == 'g') goto yy351;
+ goto yy21;
+ } else {
+ if (yych <= 't') goto yy321;
+ if (yych == 'v') goto yy352;
+ goto yy21;
+ }
+yy281:
+ yych = *++YYCURSOR;
+ if (yych == 'y') goto yy353;
+ goto yy21;
+yy282:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy354;
+ goto yy21;
+yy283:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy235;
+ goto yy21;
+yy284:
+ ++YYCURSOR;
+ YYCURSOR = YYCTXMARKER;
+ {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_NAMED;}
+yy286:
+ yych = *++YYCURSOR;
+ if (yych == ')') goto yy21;
+ goto yy356;
+yy287:
+ yych = *++YYCURSOR;
+ if (yych == '(') goto yy286;
+ goto yy21;
+yy288:
+ yych = *++YYCURSOR;
+ if (yych == 'b') goto yy357;
+ goto yy21;
+yy289:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy215;
+ goto yy21;
+yy290:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy358;
+ goto yy21;
+yy291:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy359;
+ goto yy21;
+yy292:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy360;
+ goto yy21;
+yy293:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy361;
+ goto yy21;
+yy294:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy362;
+ goto yy21;
+yy295:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy236;
+ goto yy21;
+yy296:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy363;
+ goto yy21;
+yy297:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy364;
+ goto yy21;
+yy298:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy283;
+ goto yy21;
+yy299:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy337;
+ goto yy21;
+yy300:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy365;
+ goto yy21;
+yy301:
+ yych = *++YYCURSOR;
+ if (yych != 'e') goto yy21;
+yy302:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy205;
+ goto yy21;
+yy303:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy366;
+ goto yy21;
+yy304:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy367;
+ goto yy21;
+yy305:
+ yych = *++YYCURSOR;
+ if (yych <= '@') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy368;
+ goto yy21;
+ } else {
+ if (yych <= 'F') goto yy368;
+ if (yych <= '`') goto yy21;
+ if (yych <= 'f') goto yy368;
+ goto yy21;
+ }
+yy306:
+ yych = *++YYCURSOR;
+ if (yych <= 'F') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy368;
+ if (yych <= '@') goto yy21;
+ goto yy368;
+ } else {
+ if (yych <= 'f') {
+ if (yych <= '`') goto yy21;
+ goto yy368;
+ } else {
+ if (yych == 't') goto yy215;
+ goto yy21;
+ }
+ }
+yy307:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy369;
+ goto yy21;
+yy308:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy370;
+ goto yy21;
+yy309:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy371;
+ goto yy21;
+yy310:
+ yych = *++YYCURSOR;
+ if (yych == 'w') goto yy372;
+ goto yy21;
+yy311:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy373;
+ goto yy21;
+yy312:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy374;
+ goto yy21;
+yy313:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy375;
+ goto yy21;
+yy314:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy376;
+ goto yy21;
+yy315:
+ yych = *++YYCURSOR;
+ if (yych == 'y') goto yy87;
+ goto yy21;
+yy316:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy377;
+ if (yych == 'r') goto yy378;
+ goto yy21;
+yy317:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy379;
+ goto yy21;
+yy318:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy380;
+ if (yych == 'r') goto yy381;
+ goto yy21;
+yy319:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy121;
+ goto yy21;
+yy320:
+ yych = *++YYCURSOR;
+ if (yych <= 'd') {
+ if (yych == 'a') goto yy382;
+ goto yy21;
+ } else {
+ if (yych <= 'e') goto yy383;
+ if (yych == 'l') goto yy128;
+ goto yy21;
+ }
+yy321:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy136;
+ goto yy21;
+yy322:
+ yych = *++YYCURSOR;
+ if (yych == 'k') goto yy127;
+ goto yy21;
+yy323:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy171;
+ if (yych == 'e') goto yy171;
+ goto yy21;
+yy324:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy215;
+ goto yy21;
+yy325:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy384;
+ goto yy21;
+yy326:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy330;
+ goto yy21;
+yy327:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy264;
+ goto yy21;
+yy328:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy361;
+ goto yy21;
+yy329:
+ yych = *++YYCURSOR;
+ if (yych == 'b') goto yy385;
+ goto yy21;
+yy330:
+ yych = *++YYCURSOR;
+ if (yych == 'w') goto yy386;
+ goto yy21;
+yy331:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy387;
+ goto yy21;
+yy332:
+ yych = *++YYCURSOR;
+ YYCTXMARKER = YYCURSOR;
+ if (yych <= '\'') {
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy284;
+ if (yych <= 0x1F) goto yy21;
+ goto yy284;
+ } else {
+ if (yych == '"') goto yy284;
+ if (yych <= '&') goto yy21;
+ goto yy284;
+ }
+ } else {
+ if (yych <= ',') {
+ if (yych == ')') goto yy284;
+ if (yych <= '+') goto yy21;
+ goto yy284;
+ } else {
+ if (yych <= ';') {
+ if (yych <= ':') goto yy21;
+ goto yy284;
+ } else {
+ if (yych == 'y') goto yy388;
+ goto yy21;
+ }
+ }
+ }
+yy333:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy389;
+ goto yy21;
+yy334:
+ ++YYCURSOR;
+ yych = *YYCURSOR;
+yy335:
+ if (yybm[0+yych] & 128) {
+ goto yy334;
+ }
+ if (yych == ')') goto yy390;
+ goto yy21;
+yy336:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy392;
+ goto yy21;
+yy337:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy205;
+ goto yy21;
+yy338:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy393;
+ goto yy21;
+yy339:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy394;
+ goto yy21;
+yy340:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case 'b': goto yy295;
+ case 'c': goto yy395;
+ case 'g': goto yy396;
+ case 'p': goto yy258;
+ case 's': goto yy397;
+ case 'y': goto yy388;
+ default: goto yy21;
+ }
+yy341:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy361;
+ goto yy21;
+yy342:
+ yych = *++YYCURSOR;
+ if (yych == 'm') goto yy398;
+ goto yy21;
+yy343:
+ yych = *++YYCURSOR;
+ if (yych == 'g') goto yy399;
+ goto yy21;
+yy344:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy400;
+ goto yy21;
+yy345:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy401;
+ goto yy21;
+yy346:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy402;
+ goto yy21;
+yy347:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy330;
+ goto yy21;
+yy348:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy235;
+ goto yy21;
+yy349:
+ yych = *++YYCURSOR;
+ YYCTXMARKER = YYCURSOR;
+ if (yych <= '\'') {
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy284;
+ if (yych <= 0x1F) goto yy21;
+ goto yy284;
+ } else {
+ if (yych == '"') goto yy284;
+ if (yych <= '&') goto yy21;
+ goto yy284;
+ }
+ } else {
+ if (yych <= ',') {
+ if (yych == ')') goto yy284;
+ if (yych <= '+') goto yy21;
+ goto yy284;
+ } else {
+ if (yych <= ';') {
+ if (yych <= ':') goto yy21;
+ goto yy284;
+ } else {
+ if (yych == 'd') goto yy403;
+ goto yy21;
+ }
+ }
+ }
+yy350:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy404;
+ goto yy21;
+yy351:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy377;
+ if (yych == 'r') goto yy359;
+ goto yy21;
+yy352:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy405;
+ goto yy21;
+yy353:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy406;
+ goto yy21;
+yy354:
+ yych = *++YYCURSOR;
+ if (yych == 'p') goto yy407;
+ goto yy21;
+yy355:
+ ++YYCURSOR;
+ yych = *YYCURSOR;
+yy356:
+ if (yych <= '(') {
+ if (yych <= 0x1F) {
+ if (yych == '\t') goto yy355;
+ goto yy21;
+ } else {
+ if (yych <= ' ') goto yy355;
+ if (yych == '%') goto yy355;
+ goto yy21;
+ }
+ } else {
+ if (yych <= '-') {
+ if (yych <= ')') goto yy408;
+ if (yych == ',') goto yy355;
+ goto yy21;
+ } else {
+ if (yych == '/') goto yy21;
+ if (yych <= '9') goto yy355;
+ goto yy21;
+ }
+ }
+yy357:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy81;
+ goto yy21;
+yy358:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy288;
+ goto yy21;
+yy359:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy184;
+ goto yy21;
+yy360:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy410;
+ goto yy21;
+yy361:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy205;
+ goto yy21;
+yy362:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy205;
+ goto yy21;
+yy363:
+ yych = *++YYCURSOR;
+ if (yych == 'b') goto yy295;
+ if (yych == 'g') goto yy247;
+ goto yy21;
+yy364:
+ yych = *++YYCURSOR;
+ if (yych == 'g') goto yy264;
+ goto yy21;
+yy365:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy411;
+ goto yy21;
+yy366:
+ yych = *++YYCURSOR;
+ YYCTXMARKER = YYCURSOR;
+ if (yych <= '\'') {
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy284;
+ if (yych <= 0x1F) goto yy21;
+ goto yy284;
+ } else {
+ if (yych == '"') goto yy284;
+ if (yych <= '&') goto yy21;
+ goto yy284;
+ }
+ } else {
+ if (yych <= ',') {
+ if (yych == ')') goto yy284;
+ if (yych <= '+') goto yy21;
+ goto yy284;
+ } else {
+ if (yych <= ';') {
+ if (yych <= ':') goto yy21;
+ goto yy284;
+ } else {
+ if (yych == 's') goto yy412;
+ goto yy21;
+ }
+ }
+ }
+yy367:
+ yych = *++YYCURSOR;
+ if (yych == 'w') goto yy267;
+ goto yy21;
+yy368:
+ yych = *++YYCURSOR;
+ if (yych <= '@') {
+ if (yych <= '/') goto yy21;
+ if (yych <= '9') goto yy413;
+ goto yy21;
+ } else {
+ if (yych <= 'F') goto yy413;
+ if (yych <= '`') goto yy21;
+ if (yych <= 'f') goto yy413;
+ goto yy21;
+ }
+yy369:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy330;
+ goto yy21;
+yy370:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy415;
+ goto yy21;
+yy371:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy416;
+ goto yy21;
+yy372:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy417;
+ goto yy21;
+yy373:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy418;
+ goto yy21;
+yy374:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy419;
+ goto yy21;
+yy375:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy420;
+ goto yy21;
+yy376:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy201;
+ goto yy21;
+yy377:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy421;
+ goto yy21;
+yy378:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy171;
+ if (yych == 'e') goto yy422;
+ goto yy21;
+yy379:
+ yych = *++YYCURSOR;
+ if (yych == 'g') goto yy185;
+ goto yy21;
+yy380:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy423;
+ goto yy21;
+yy381:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy424;
+ if (yych == 'c') goto yy196;
+ goto yy21;
+yy382:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy210;
+ goto yy21;
+yy383:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy264;
+ goto yy21;
+yy384:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy425;
+ goto yy21;
+yy385:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy426;
+ goto yy21;
+yy386:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy427;
+ goto yy21;
+yy387:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy417;
+ goto yy21;
+yy388:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy428;
+ goto yy21;
+yy389:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy217;
+ goto yy21;
+yy390:
+ ++YYCURSOR;
+ {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_HSL;}
+yy392:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy319;
+ goto yy21;
+yy393:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy429;
+ goto yy21;
+yy394:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy430;
+ goto yy21;
+yy395:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy431;
+ if (yych == 'y') goto yy87;
+ goto yy21;
+yy396:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy432;
+ if (yych == 'r') goto yy378;
+ goto yy21;
+yy397:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case 'a': goto yy382;
+ case 'e': goto yy383;
+ case 'k': goto yy127;
+ case 'l': goto yy433;
+ case 't': goto yy131;
+ default: goto yy21;
+ }
+yy398:
+ yych = *++YYCURSOR;
+ switch (yych) {
+ case 'a': goto yy434;
+ case 'b': goto yy295;
+ case 'o': goto yy435;
+ case 'p': goto yy436;
+ case 's': goto yy437;
+ case 't': goto yy321;
+ case 'v': goto yy352;
+ default: goto yy21;
+ }
+yy399:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy240;
+ goto yy21;
+yy400:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy438;
+ goto yy21;
+yy401:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy439;
+ goto yy21;
+yy402:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy132;
+ goto yy21;
+yy403:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy440;
+ goto yy21;
+yy404:
+ yych = *++YYCURSOR;
+ YYCTXMARKER = YYCURSOR;
+ if (yych <= '\'') {
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy284;
+ if (yych <= 0x1F) goto yy21;
+ goto yy284;
+ } else {
+ if (yych == '"') goto yy284;
+ if (yych <= '&') goto yy21;
+ goto yy284;
+ }
+ } else {
+ if (yych <= ',') {
+ if (yych == ')') goto yy284;
+ if (yych <= '+') goto yy21;
+ goto yy284;
+ } else {
+ if (yych <= ';') {
+ if (yych <= ':') goto yy21;
+ goto yy284;
+ } else {
+ if (yych == 'r') goto yy319;
+ goto yy21;
+ }
+ }
+ }
+yy405:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy441;
+ goto yy21;
+yy406:
+ yych = *++YYCURSOR;
+ if (yych == 'w') goto yy442;
+ goto yy21;
+yy407:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy443;
+ goto yy21;
+yy408:
+ ++YYCURSOR;
+ {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_RGB;}
+yy410:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy220;
+ goto yy21;
+yy411:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy439;
+ goto yy21;
+yy412:
+ yych = *++YYCURSOR;
+ if (yych == 'm') goto yy444;
+ goto yy21;
+yy413:
+ ++YYCURSOR;
+ {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_HEX;}
+yy415:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy445;
+ goto yy21;
+yy416:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy446;
+ goto yy21;
+yy417:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy121;
+ goto yy21;
+yy418:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy439;
+ goto yy21;
+yy419:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy235;
+ goto yy21;
+yy420:
+ yych = *++YYCURSOR;
+ if (yych == 'w') goto yy248;
+ goto yy21;
+yy421:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy447;
+ goto yy21;
+yy422:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy132;
+ if (yych == 'y') goto yy205;
+ goto yy21;
+yy423:
+ yych = *++YYCURSOR;
+ if (yych == 'v') goto yy448;
+ goto yy21;
+yy424:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy149;
+ goto yy21;
+yy425:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy201;
+ goto yy21;
+yy426:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy337;
+ goto yy21;
+yy427:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy419;
+ goto yy21;
+yy428:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy449;
+ goto yy21;
+yy429:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy450;
+ goto yy21;
+yy430:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy451;
+ goto yy21;
+yy431:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy133;
+ goto yy21;
+yy432:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy452;
+ goto yy21;
+yy433:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy453;
+ goto yy21;
+yy434:
+ yych = *++YYCURSOR;
+ if (yych == 'q') goto yy454;
+ goto yy21;
+yy435:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy455;
+ goto yy21;
+yy436:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy120;
+ goto yy21;
+yy437:
+ yych = *++YYCURSOR;
+ if (yych <= 'k') {
+ if (yych == 'e') goto yy383;
+ goto yy21;
+ } else {
+ if (yych <= 'l') goto yy456;
+ if (yych == 'p') goto yy130;
+ goto yy21;
+ }
+yy438:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy202;
+ goto yy21;
+yy439:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy235;
+ goto yy21;
+yy440:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy457;
+ goto yy21;
+yy441:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy458;
+ goto yy21;
+yy442:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy459;
+ goto yy21;
+yy443:
+ yych = *++YYCURSOR;
+ if (yych == 'f') goto yy460;
+ goto yy21;
+yy444:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy461;
+ goto yy21;
+yy445:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy235;
+ goto yy21;
+yy446:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy462;
+ goto yy21;
+yy447:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy331;
+ goto yy21;
+yy448:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy264;
+ goto yy21;
+yy449:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy129;
+ goto yy21;
+yy450:
+ yych = *++YYCURSOR;
+ YYCTXMARKER = YYCURSOR;
+ if (yych <= '\'') {
+ if (yych <= ' ') {
+ if (yych == '\t') goto yy284;
+ if (yych <= 0x1F) goto yy21;
+ goto yy284;
+ } else {
+ if (yych == '"') goto yy284;
+ if (yych <= '&') goto yy21;
+ goto yy284;
+ }
+ } else {
+ if (yych <= ',') {
+ if (yych == ')') goto yy284;
+ if (yych <= '+') goto yy21;
+ goto yy284;
+ } else {
+ if (yych <= ';') {
+ if (yych <= ':') goto yy21;
+ goto yy284;
+ } else {
+ if (yych == 'b') goto yy463;
+ goto yy21;
+ }
+ }
+ }
+yy451:
+ yych = *++YYCURSOR;
+ if (yych == 'f') goto yy464;
+ goto yy21;
+yy452:
+ yych = *++YYCURSOR;
+ if (yych == 'd') goto yy465;
+ goto yy21;
+yy453:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy466;
+ goto yy21;
+yy454:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy467;
+ goto yy21;
+yy455:
+ yych = *++YYCURSOR;
+ if (yych == 'c') goto yy196;
+ goto yy21;
+yy456:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy468;
+ goto yy21;
+yy457:
+ yych = *++YYCURSOR;
+ if (yych == 'b') goto yy205;
+ goto yy21;
+yy458:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy469;
+ goto yy21;
+yy459:
+ yych = *++YYCURSOR;
+ if (yych == 'i') goto yy470;
+ goto yy21;
+yy460:
+ yych = *++YYCURSOR;
+ if (yych == 'f') goto yy205;
+ goto yy21;
+yy461:
+ yych = *++YYCURSOR;
+ if (yych == 'k') goto yy235;
+ goto yy21;
+yy462:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy471;
+ goto yy21;
+yy463:
+ yych = *++YYCURSOR;
+ if (yych == 'l') goto yy472;
+ goto yy21;
+yy464:
+ yych = *++YYCURSOR;
+ if (yych == 'f') goto yy269;
+ goto yy21;
+yy465:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy473;
+ goto yy21;
+yy466:
+ yych = *++YYCURSOR;
+ if (yych == 'e') goto yy162;
+ goto yy21;
+yy467:
+ yych = *++YYCURSOR;
+ if (yych == 'a') goto yy474;
+ goto yy21;
+yy468:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy232;
+ goto yy21;
+yy469:
+ yych = *++YYCURSOR;
+ if (yych == 't') goto yy392;
+ goto yy21;
+yy470:
+ yych = *++YYCURSOR;
+ if (yych == 'p') goto yy205;
+ goto yy21;
+yy471:
+ yych = *++YYCURSOR;
+ if (yych == 'm') goto yy475;
+ goto yy21;
+yy472:
+ yych = *++YYCURSOR;
+ if (yych == 'u') goto yy476;
+ goto yy21;
+yy473:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy477;
+ goto yy21;
+yy474:
+ yych = *++YYCURSOR;
+ if (yych == 'm') goto yy308;
+ goto yy21;
+yy475:
+ yych = *++YYCURSOR;
+ if (yych == 'o') goto yy478;
+ goto yy21;
+yy476:
+ yych = *++YYCURSOR;
+ if (yych == 's') goto yy479;
+ goto yy21;
+yy477:
+ yych = *++YYCURSOR;
+ if (yych == 'r') goto yy480;
+ goto yy21;
+yy478:
+ yych = *++YYCURSOR;
+ if (yych == 'n') goto yy121;
+ goto yy21;
+yy479:
+ yych = *++YYCURSOR;
+ if (yych == 'h') goto yy205;
+ goto yy21;
+yy480:
+ yych = *++YYCURSOR;
+ if (yych != 'o') goto yy21;
+ yych = *++YYCURSOR;
+ if (yych != 'd') goto yy21;
+ ++YYCURSOR;
+ if ((yych = *YYCURSOR) == 'y') goto yy388;
+ goto yy21;
+ }
+
+
+comment:
+
+ {
+ guchar yych;
+ yych = *YYCURSOR;
+ if (yych <= 0xDF) {
+ if (yych <= '*') {
+ if (yych <= 0x00) goto yy486;
+ if (yych <= ')') goto yy488;
+ goto yy490;
+ } else {
+ if (yych <= 0x7F) goto yy488;
+ if (yych >= 0xC2) goto yy491;
+ }
+ } else {
+ if (yych <= 0xF0) {
+ if (yych <= 0xE0) goto yy492;
+ if (yych <= 0xEF) goto yy493;
+ goto yy494;
+ } else {
+ if (yych <= 0xF3) goto yy495;
+ if (yych <= 0xF4) goto yy496;
+ }
+ }
+yy485:
+yy486:
+ ++YYCURSOR;
+ {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_EOF;}
+yy488:
+ ++YYCURSOR;
+yy489:
+ { goto comment; }
+yy490:
+ yych = *++YYCURSOR;
+ if (yych == '/') goto yy497;
+ goto yy489;
+yy491:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy485;
+ if (yych <= 0xBF) goto yy488;
+ goto yy485;
+yy492:
+ yych = *++YYCURSOR;
+ if (yych <= 0x9F) goto yy485;
+ if (yych <= 0xBF) goto yy491;
+ goto yy485;
+yy493:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy485;
+ if (yych <= 0xBF) goto yy491;
+ goto yy485;
+yy494:
+ yych = *++YYCURSOR;
+ if (yych <= 0x8F) goto yy485;
+ if (yych <= 0xBF) goto yy493;
+ goto yy485;
+yy495:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy485;
+ if (yych <= 0xBF) goto yy493;
+ goto yy485;
+yy496:
+ yych = *++YYCURSOR;
+ if (yych <= 0x7F) goto yy485;
+ if (yych <= 0x8F) goto yy493;
+ goto yy485;
+yy497:
+ ++YYCURSOR;
+ { goto standard; }
+ }
+
+}
+
+/* TODO: check for " ' \t space around named */
+/**
+ * gstyle_colorlexer_parse:
+ * @data: a constant string.
+ *
+ * Parse a string and return an array of #GstyleColorItem.
+ *
+ * Returns: (element-type GstyleColorItem) (transfer full): a #GPtrArray of #GstyleColorItem.
+ *
+ */
+GPtrArray *
+gstyle_colorlexer_parse (const gchar *data)
+{
+ GPtrArray *ar;
+ GstyleColorItem *item;
+ GstyleColorScanner s;
+ gint token;
+
+ if (gstyle_str_empty0 (data))
+ return NULL;
+
+ ar = g_ptr_array_new_full (PARSE_ARRAY_INITIAL_SIZE, (GDestroyNotify)gstyle_color_item_unref);
+ gstyle_colorlexer_init (&s, data);
+
+ while((token = gstyle_colorlexer_scan (&s)) != GSTYLE_COLOR_TOKEN_EOF)
+ {
+ if (token != GSTYLE_COLOR_TOKEN_HEX && *s.start == '#')
+ ++s.start;
+
+ item = gstyle_color_item_new (NULL, s.start - data, s.cursor - s.start);
+ g_ptr_array_add (ar, item);
+ }
+
+ return ar;
+}
diff --git a/contrib/gstyle/gstyle-colorlexer.h b/contrib/gstyle/gstyle-colorlexer.h
new file mode 100644
index 0000000..26d4f64
--- /dev/null
+++ b/contrib/gstyle/gstyle-colorlexer.h
@@ -0,0 +1,48 @@
+/* gstyle-colorlexer.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_COLORLEXER_H
+#define GSTYLE_COLORLEXER_H
+
+#include "gstyle-types.h"
+#include "gstyle-color.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+ const gchar *start;
+ const gchar *cursor;
+ const gchar *ptr;
+ const gchar *ptr_ctx;
+} GstyleColorScanner;
+
+typedef enum
+{
+ GSTYLE_COLOR_TOKEN_EOF,
+ GSTYLE_COLOR_TOKEN_HEX,
+ GSTYLE_COLOR_TOKEN_RGB,
+ GSTYLE_COLOR_TOKEN_HSL,
+ GSTYLE_COLOR_TOKEN_NAMED
+} GstyleColorToken;
+
+GPtrArray *gstyle_colorlexer_parse (const gchar *data);
+
+G_END_DECLS
+
+#endif /* GSTYLE_COLORLEXER_H */
diff --git a/contrib/gstyle/gstyle-colorlexer.re b/contrib/gstyle/gstyle-colorlexer.re
new file mode 100644
index 0000000..d8371c8
--- /dev/null
+++ b/contrib/gstyle/gstyle-colorlexer.re
@@ -0,0 +1,142 @@
+/* gstyle-colorlexer.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-colorlexer"
+
+#include <glib.h>
+
+#include "gstyle-color.h"
+#include "gstyle-color-item.h"
+#include "gstyle-types.h"
+#include "gstyle-utils.h"
+
+#include "gstyle-colorlexer.h"
+
+#define PARSE_ARRAY_INITIAL_SIZE 32
+
+#define YYCTYPE guchar
+#define YYCURSOR cursor
+#define YYMARKER s->ptr
+#define YYCTXMARKER s->ptr_ctx
+
+static void
+gstyle_colorlexer_init (GstyleColorScanner *s,
+ const gchar *data)
+{
+ s->start = data;
+ s->cursor = data;
+}
+
+static gint
+gstyle_colorlexer_scan (GstyleColorScanner *s)
+{
+ const gchar *cursor = s->cursor;
+
+standard:
+ s->start = cursor;
+
+/*!re2c
+ re2c:define:YYCTYPE = guchar;
+ re2c:yyfill:enable = 0;
+ re2c:indent:top = 2;
+
+ params = [0-9., \t%];
+ hex = [0-9a-fA-F];
+ named = "aliceblue" | "antiquewhite" | "aqua" | "aquamarine" | "azure" | "beige" | "bisque" | "black"|
"blanchedalmond" |
+ "blue" | "blueviolet" | "brown" | "burlywood" | "cadetblue" | "chartreuse" | "chocolate" | "coral"
|
+ "cornflowerblue" | "cornsilk" | "crimson" | "cyan" | "darkblue" | "darkcyan" | "darkgoldenrod" |
"darkgray" |
+ "darkgreen" | "darkgrey" | "darkkhaki" | "darkmagenta" | "darkolivegreen" | "darkorange" |
"darkorchid" |
+ "darkred" | "darksalmon" | "darkseagreen" | "darkslateblue" | "darkslategray" | "darkslategrey" |
"darkturquoise" |
+ "darkviolet" | "deeppink" | "deepskyblue" | "dimgray" | "dimgrey" | "dodgerblue" | "firebrick" |
"floralwhite" |
+ "forestgreen" | "fuchsia" | "gainsboro" | "ghostwhite" | "gold" | "goldenrod" | "gray" | "green" |
"greenyellow" | "grey" |
+ "honeydew" | "hotpink" | "indianred" | "indigo" | "ivory" | "khaki" | "lavender" | "lavenderblush"
| "lawngreen" |
+ "lemonchiffon" | "lightblue" | "lightcoral" | "lightcyan" | "lightgoldenrodyellow" | "lightgray" |
"lightgreen" | "lightgrey" |
+ "lightpink" | "lightsalmon" | "lightseagreen" | "lightskyblue" | "lightslategray" |
"lightslategrey" | "lightsteelblue" |
+ "lightyellow" | "lime" | "limegreen" | "linen" | "magenta" | "maroon" | "mediumaquamarine" |
"mediumblue" | "mediumorchid" |
+ "mediumpurple" | "mediumseagreen" | "mediumslateblue" | "mediumspringgreen" | "mediumturquoise" |
"mediumvioletred" |
+ "midnightblue" | "mintcream" | "mistyrose" | "moccasin" | "navajowhite" | "navy" | "oldlace" |
"olive" | "olivedrab" |
+ "orange" | "orangered" | "orchid" | "palegoldenrod" | "palegreen" | "paleturquoise" |
"palevioletred" | "papayawhip" |
+ "peachpuff" | "peru" | "pink" | "plum" | "powderblue" | "purple" | "red" | "rosybrown" |
"royalblue" | "saddlebrown" |
+ "salmon" | "sandybrown" | "seagreen" | "seashell" | "sienna" | "silver" | "skyblue" | "slateblue"
| "slategray" |
+ "slategrey" | "snow" | "springgreen" | "steelblue" | "tan" | "teal" | "thistle" | "tomato" |
"turquoise" | "violet" |
+ "wheat" | "white" | "whitesmoke" | "yellow" | "yellowgreen";
+
+ "/*" { goto comment; }
+ [ \t"',:(] [ \t]* { s->start = cursor;goto color; }
+
+ "\000" {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_EOF;}
+ [^] { goto standard; }
+*/
+
+color:
+/*!re2c
+
+ "#" hex{3} {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_HEX;}
+ "#" hex{6} {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_HEX;}
+ "#"? "rgb" "a"? "(" params+ ")" {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_RGB;}
+ "#"? "hsl" "a"? "(" params+ ")" {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_HSL;}
+ "#"? named / ["' \t,;)] {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_NAMED;}
+
+ "\000" {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_EOF;}
+ [^] { goto standard; }
+*/
+
+comment:
+/*!re2c
+
+ "*/" { goto standard; }
+ "\000" {s->cursor = cursor; return GSTYLE_COLOR_TOKEN_EOF;}
+ [^] { goto comment; }
+*/
+}
+
+/* TODO: check for " ' \t space around named */
+/**
+ * gstyle_colorlexer_parse:
+ * @data: a constant string.
+ *
+ * Parse a string and return an array of #GstyleColorItem.
+ *
+ * Returns: (element-type GstyleColorItem) (transfer full): a #GPtrArray of #GstyleColorItem.
+ *
+ */
+GPtrArray *
+gstyle_colorlexer_parse (const gchar *data)
+{
+ GPtrArray *ar;
+ GstyleColorItem *item;
+ GstyleColorScanner s;
+ gint token;
+
+ if (gstyle_str_empty0 (data))
+ return NULL;
+
+ ar = g_ptr_array_new_full (PARSE_ARRAY_INITIAL_SIZE, (GDestroyNotify)gstyle_color_item_unref);
+ gstyle_colorlexer_init (&s, data);
+
+ while((token = gstyle_colorlexer_scan (&s)) != GSTYLE_COLOR_TOKEN_EOF)
+ {
+ if (token != GSTYLE_COLOR_TOKEN_HEX && *s.start == '#')
+ ++s.start;
+
+ item = gstyle_color_item_new (NULL, s.start - data, s.cursor - s.start);
+ g_ptr_array_add (ar, item);
+ }
+
+ return ar;
+}
diff --git a/contrib/gstyle/gstyle-css-provider.c b/contrib/gstyle/gstyle-css-provider.c
new file mode 100644
index 0000000..8a108f2
--- /dev/null
+++ b/contrib/gstyle/gstyle-css-provider.c
@@ -0,0 +1,158 @@
+/* gstyle-css-provider.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-css-provider"
+
+#include "gstyle-css-provider.h"
+
+struct _GstyleCssProvider
+{
+ GtkCssProvider parent_instance;
+};
+
+G_DEFINE_TYPE (GstyleCssProvider, gstyle_css_provider, GTK_TYPE_CSS_PROVIDER)
+
+enum {
+ PROP_0,
+ N_PROPS
+};
+
+/* static GParamSpec *properties [N_PROPS]; */
+
+static GstyleCssProvider *default_provider = NULL;
+
+static void
+parsing_error (GstyleCssProvider *self,
+ GtkCssSection *section,
+ const GError *error,
+ GtkCssProvider *provider)
+{
+ g_autofree gchar *uri = NULL;
+ GFile *file;
+ guint line = 0;
+ guint line_offset = 0;
+
+ g_assert (GSTYLE_IS_CSS_PROVIDER (self));
+ g_assert (GTK_IS_CSS_PROVIDER (provider));
+ g_assert (error != NULL);
+
+ if (section != NULL)
+ {
+ file = gtk_css_section_get_file (section);
+ uri = g_file_get_uri (file);
+ line = gtk_css_section_get_start_line (section);
+ line_offset = gtk_css_section_get_start_position (section);
+ g_warning ("Parsing Error: %s @ %u:%u: %s", uri, line, line_offset, error->message);
+ }
+ else
+ {
+ g_warning ("%s", error->message);
+ }
+}
+
+static void
+default_provider_weak_notify (gpointer unused,
+ GstyleCssProvider *provider)
+{
+ g_assert (GTK_IS_CSS_PROVIDER (provider));
+
+ g_warn_if_fail (g_atomic_pointer_compare_and_exchange (&default_provider, provider, NULL));
+}
+
+GstyleCssProvider *
+gstyle_css_provider_init_default (GdkScreen *screen)
+{
+ g_return_val_if_fail (screen != NULL, NULL);
+
+ if (default_provider == NULL)
+ {
+ default_provider = g_object_new (GSTYLE_TYPE_CSS_PROVIDER, NULL);
+ g_object_weak_ref (G_OBJECT (default_provider), (GWeakNotify)default_provider_weak_notify, NULL);
+
+ g_assert ( GSTYLE_IS_CSS_PROVIDER (default_provider));
+
+ gtk_style_context_add_provider_for_screen (screen,
+ GTK_STYLE_PROVIDER (default_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_FALLBACK);
+ return default_provider;
+ }
+
+ g_assert ( GSTYLE_IS_CSS_PROVIDER (default_provider));
+ return g_object_ref (default_provider);
+}
+
+GstyleCssProvider *
+gstyle_css_provider_new (void)
+{
+ return g_object_new (GSTYLE_TYPE_CSS_PROVIDER, NULL);
+}
+
+static void
+gstyle_css_provider_finalize (GObject *object)
+{
+ /* GstyleCssProvider *self = (GstyleCssProvider *)object; */
+
+ G_OBJECT_CLASS (gstyle_css_provider_parent_class)->finalize (object);
+}
+
+static void
+gstyle_css_provider_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ /* GstyleCssProvider *self = GSTYLE_CSS_PROVIDER (object); */
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_css_provider_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ /* GstyleCssProvider *self = GSTYLE_CSS_PROVIDER (object); */
+
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_css_provider_class_init (GstyleCssProviderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gstyle_css_provider_finalize;
+ object_class->get_property = gstyle_css_provider_get_property;
+ object_class->set_property = gstyle_css_provider_set_property;
+}
+
+static void
+gstyle_css_provider_init (GstyleCssProvider *self)
+{
+ gtk_css_provider_load_from_resource (GTK_CSS_PROVIDER (self), "/org/gnome/libgstyle/theme/gstyle.css");
+ g_signal_connect_swapped (self, "parsing-error", G_CALLBACK (parsing_error), self);
+}
diff --git a/contrib/gstyle/gstyle-css-provider.h b/contrib/gstyle/gstyle-css-provider.h
new file mode 100644
index 0000000..f3a8275
--- /dev/null
+++ b/contrib/gstyle/gstyle-css-provider.h
@@ -0,0 +1,37 @@
+/* gstyle-css-provider.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_CSS_PROVIDER_H
+#define GSTYLE_CSS_PROVIDER_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_CSS_PROVIDER (gstyle_css_provider_get_type())
+
+G_DECLARE_FINAL_TYPE (GstyleCssProvider, gstyle_css_provider, GSTYLE, CSS_PROVIDER, GtkCssProvider)
+
+GstyleCssProvider *gstyle_css_provider_init_default (GdkScreen *screen);
+GstyleCssProvider *gstyle_css_provider_new (void);
+
+G_END_DECLS
+
+#endif /* GSTYLE_CSS_PROVIDER_H */
+
diff --git a/contrib/gstyle/gstyle-eyedropper.c b/contrib/gstyle/gstyle-eyedropper.c
new file mode 100644
index 0000000..d3ee4b3
--- /dev/null
+++ b/contrib/gstyle/gstyle-eyedropper.c
@@ -0,0 +1,400 @@
+/* gstyle-eyedropper.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This source code is first based on the now deprecated
+ * gtk eyedropper source code available at:
+ * https://git.gnome.org/browse/gtk+/tree/gtk/deprecated/gtkcolorsel.c#n1705
+ */
+
+#define G_LOG_DOMAIN "gstyle-eyedropper"
+
+#include <gdk/gdk.h>
+
+#include "gstyle-color.h"
+#include "gstyle-eyedropper.h"
+
+struct _GstyleEyedropper
+{
+ GtkWindow parent_instance;
+
+ GtkWidget *source;
+ GtkWidget *window;
+ GdkCursor *cursor;
+ GdkSeat *seat;
+
+ gulong key_handler_id;
+ gulong grab_broken_handler_id;
+ gulong motion_notify_handler_id;
+ gulong pointer_pressed_handler_id;
+ gulong pointer_released_handler_id;
+};
+
+G_DEFINE_TYPE (GstyleEyedropper, gstyle_eyedropper, GTK_TYPE_WINDOW)
+
+enum {
+ COLOR_PICKED,
+ GRAB_RELEASED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_SOURCE_EVENT,
+ N_PROPS
+};
+
+static guint signals [LAST_SIGNAL];
+static GParamSpec *properties [N_PROPS];
+
+static void
+get_rgba_at_cursor (GstyleEyedropper *self,
+ GdkScreen *screen,
+ GdkDevice *device,
+ gint x,
+ gint y,
+ GdkRGBA *rgba)
+{
+ GdkWindow *window;
+ GdkPixbuf *pixbuf;
+ guchar *pixels;
+
+ g_assert (GSTYLE_IS_EYEDROPPER (self));
+ g_assert (GDK_IS_SCREEN (screen));
+ g_assert (GDK_IS_DEVICE (device));
+
+ window = gdk_screen_get_root_window (screen);
+ pixbuf = gdk_pixbuf_get_from_window (window, x, y, 1, 1);
+ if (!pixbuf)
+ {
+ window = gdk_device_get_window_at_position (device, &x, &y);
+ if (!window)
+ return;
+
+ pixbuf = gdk_pixbuf_get_from_window (window, x, y, 1, 1);
+ if (!pixbuf)
+ return;
+ }
+
+ g_assert (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
+ g_assert (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
+
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+ rgba->red = pixels[0] / 255.0;
+ rgba->green = pixels[1] / 255.0;
+ rgba->blue = pixels[2] /255.0;
+
+ g_object_unref (pixbuf);
+}
+
+static void
+release_grab (GstyleEyedropper *self)
+{
+ g_assert (GSTYLE_IS_EYEDROPPER (self));
+
+ if (self->key_handler_id)
+ {
+ g_signal_handler_disconnect (self->window, self->key_handler_id);
+ self->key_handler_id = 0;
+ }
+
+ if (self->grab_broken_handler_id)
+ {
+ g_signal_handler_disconnect (self->window, self->grab_broken_handler_id);
+ self->grab_broken_handler_id = 0;
+ }
+
+ if (self->motion_notify_handler_id)
+ {
+ g_signal_handler_disconnect (self->window, self->motion_notify_handler_id);
+ self->motion_notify_handler_id = 0;
+ }
+
+ if (self->pointer_released_handler_id)
+ {
+ g_signal_handler_disconnect (self->window, self->pointer_released_handler_id);
+ self->pointer_released_handler_id = 0;
+ }
+
+ gtk_grab_remove (self->window);
+ gdk_seat_ungrab (self->seat);
+
+ g_signal_emit (self, signals [GRAB_RELEASED], 0);
+}
+
+static void
+gstyle_eyedropper_pointer_motion_notify_cb (GstyleEyedropper *self,
+ GdkEventMotion *event,
+ GtkWindow *window)
+{
+ GdkRGBA rgba;
+
+ g_assert (GSTYLE_IS_EYEDROPPER (self));
+ g_assert (event != NULL);
+ g_assert (GTK_IS_WINDOW (window));
+
+ get_rgba_at_cursor (self,
+ gdk_event_get_screen ((GdkEvent *) event),
+ gdk_event_get_device ((GdkEvent *) event),
+ event->x_root, event->y_root, &rgba);
+
+ g_signal_emit (self, signals [COLOR_PICKED], 0, &rgba);
+}
+
+static gboolean
+gstyle_eyedropper_pointer_released_cb (GstyleEyedropper *self,
+ GdkEventButton *event,
+ GtkWindow *window)
+{
+ g_assert (GSTYLE_IS_EYEDROPPER (self));
+ g_assert (event != NULL);
+ g_assert (GTK_IS_WINDOW (window));
+
+ release_grab (self);
+
+ return GDK_EVENT_STOP;
+}
+
+static gboolean
+gstyle_eyedropper_pointer_pressed_cb (GstyleEyedropper *self,
+ GdkEventButton *event,
+ GtkWindow *window)
+{
+ GdkRGBA rgba;
+
+ g_assert (GSTYLE_IS_EYEDROPPER (self));
+ g_assert (event != NULL);
+ g_assert (GTK_IS_WINDOW (window));
+
+ if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY)
+ {
+ self->motion_notify_handler_id =
+ g_signal_connect_swapped (window, "motion-notify-event",
+ G_CALLBACK (gstyle_eyedropper_pointer_motion_notify_cb), self);
+
+ self->pointer_released_handler_id =
+ g_signal_connect_swapped (window, "button-release-event",
+ G_CALLBACK (gstyle_eyedropper_pointer_released_cb), self);
+
+ g_signal_handler_disconnect (self->window, self->pointer_pressed_handler_id);
+ self->pointer_pressed_handler_id = 0;
+
+ get_rgba_at_cursor (self,
+ gdk_event_get_screen ((GdkEvent *) event),
+ gdk_event_get_device ((GdkEvent *) event),
+ event->x_root, event->y_root, &rgba);
+
+ g_signal_emit (self, signals [COLOR_PICKED], 0, &rgba);
+
+ return GDK_EVENT_STOP;
+ }
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+gstyle_eyedropper_key_pressed_cb (GstyleEyedropper *self,
+ GdkEventKey *event,
+ GtkWindow *window)
+{
+ g_assert (GSTYLE_IS_EYEDROPPER (self));
+ g_assert (event != NULL);
+ g_assert (GTK_IS_WINDOW (window));
+
+ /* TODO: handle cursor and picker trigger keys */
+ switch (event->keyval)
+ {
+ case GDK_KEY_Escape:
+ release_grab (self);
+ break;
+
+ default:
+ break;
+ }
+
+ return GDK_EVENT_STOP;
+}
+
+static gboolean
+gstyle_eyedropper_grab_broken_cb (GstyleEyedropper *self,
+ GdkEventKey *event,
+ GtkWidget *window)
+{
+ g_assert (GSTYLE_IS_EYEDROPPER (self));
+ g_assert (event != NULL);
+ g_assert (GTK_IS_WINDOW (window));
+
+ release_grab (self);
+
+ return GDK_EVENT_STOP;
+}
+
+void
+gstyle_eyedropper_set_source_event (GstyleEyedropper *self,
+ GdkEvent *event)
+{
+ GdkScreen *screen;
+ GtkWidget *source;
+ GdkGrabStatus status;
+
+ g_return_if_fail (GSTYLE_IS_EYEDROPPER (self));
+ g_return_if_fail (event != NULL);
+
+ self->seat = g_object_ref (gdk_event_get_seat (event));
+ source = gtk_get_event_widget (event);
+ screen = gdk_event_get_screen (event);
+
+ g_clear_object (&self->window);
+ g_clear_object (&self->cursor);
+
+ self->window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_screen (GTK_WINDOW (self->window), screen);
+ gtk_window_resize (GTK_WINDOW (self->window), 1, 1);
+ gtk_window_move (GTK_WINDOW (self->window), -1, -1);
+ gtk_widget_show (self->window);
+
+ gtk_widget_add_events (self->window,
+ GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
+
+ self->cursor = gdk_cursor_new_from_name (gdk_screen_get_display (screen), "cell");
+ gtk_grab_add (self->window);
+ status = gdk_seat_grab (self->seat,
+ gtk_widget_get_window (source),
+ GDK_SEAT_CAPABILITY_ALL,
+ FALSE,
+ self->cursor,
+ event,
+ NULL, NULL);
+
+ if (status != GDK_GRAB_SUCCESS)
+ {
+ g_warning ("grab failed status:%i\n", status);
+ return;
+ }
+
+ self->pointer_pressed_handler_id =
+ g_signal_connect_swapped (self->window,
+ "button-press-event",
+ G_CALLBACK (gstyle_eyedropper_pointer_pressed_cb),
+ self);
+
+ self->key_handler_id =
+ g_signal_connect_swapped (self->window,
+ "key-press-event",
+ G_CALLBACK (gstyle_eyedropper_key_pressed_cb),
+ self);
+
+ self->grab_broken_handler_id =
+ g_signal_connect_swapped (self->window,
+ "grab-broken-event",
+ G_CALLBACK (gstyle_eyedropper_grab_broken_cb),
+ self);
+}
+
+GstyleEyedropper *
+gstyle_eyedropper_new (GdkEvent *event)
+{
+ return g_object_new (GSTYLE_TYPE_EYEDROPPER,
+ "source-event", event,
+ NULL);
+}
+
+static void
+gstyle_eyedropper_finalize (GObject *object)
+{
+ GstyleEyedropper *self = GSTYLE_EYEDROPPER (object);
+
+ g_clear_object (&self->window);
+ g_clear_object (&self->cursor);
+ gdk_seat_ungrab (self->seat);
+
+ G_OBJECT_CLASS (gstyle_eyedropper_parent_class)->finalize (object);
+}
+
+static void
+gstyle_eyedropper_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleEyedropper *self = GSTYLE_EYEDROPPER (object);
+ GdkEvent *event;
+
+ switch (prop_id)
+ {
+ case PROP_SOURCE_EVENT:
+ event = g_value_get_boxed (value);
+ gstyle_eyedropper_set_source_event (self, event);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_eyedropper_class_init (GstyleEyedropperClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gstyle_eyedropper_finalize;
+ object_class->set_property = gstyle_eyedropper_set_property;
+
+ /**
+ * GstyleEyedropper::color-picked:
+ * @self: A #GstyleEyedropper.
+ * @rgba: a #GdkRGBA color.
+ *
+ * This signal is emitted when you click to pick a color.
+ */
+ signals [COLOR_PICKED] = g_signal_new ("color-picked",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ GDK_TYPE_RGBA);
+
+ /**
+ * GstyleEyedropper::grab-released:
+ * @self: A #GstyleEyedropper.
+ *
+ * This signal is emitted when you release the grab by hitting 'Esc'.
+ */
+ signals [GRAB_RELEASED] = g_signal_new ("grab-released",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ properties [PROP_SOURCE_EVENT] =
+ g_param_spec_boxed ("source-event",
+ "source-event",
+ "the event generated when triggering the picker widget",
+ GDK_TYPE_EVENT,
+ (G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gstyle_eyedropper_init (GstyleEyedropper *self)
+{
+}
diff --git a/contrib/gstyle/gstyle-eyedropper.h b/contrib/gstyle/gstyle-eyedropper.h
new file mode 100644
index 0000000..bab4905
--- /dev/null
+++ b/contrib/gstyle/gstyle-eyedropper.h
@@ -0,0 +1,35 @@
+/* gstyle-eyedropper.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_EYEDROPPER_H
+#define GSTYLE_EYEDROPPER_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_EYEDROPPER (gstyle_eyedropper_get_type())
+
+G_DECLARE_FINAL_TYPE (GstyleEyedropper, gstyle_eyedropper, GSTYLE, EYEDROPPER, GtkWindow)
+
+GstyleEyedropper *gstyle_eyedropper_new (GdkEvent *event);
+
+G_END_DECLS
+
+#endif /* GSTYLE_EYEDROPPER_H */
diff --git a/contrib/gstyle/gstyle-hsv.c b/contrib/gstyle/gstyle-hsv.c
new file mode 100644
index 0000000..53e2b03
--- /dev/null
+++ b/contrib/gstyle/gstyle-hsv.c
@@ -0,0 +1,64 @@
+/* gstyle-hsv.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-hsv"
+
+#include "gstyle-hsv.h"
+
+G_DEFINE_BOXED_TYPE (GstyleHSV, gstyle_hsv, gstyle_hsv_copy, gstyle_hsv_free)
+
+/**
+ * GstyleHSV:
+ * @h: color hue in the range [0, 360[ degrees.
+ * @s: color saturation in the range [0, 1].
+ * @v: color value in the range [0, 1].
+ * @alpha: The opacity of the color in [0, 1] range.
+ *
+ * A #GstyleHSV is used to represent a color in
+ * the HSV (also called HSB) colorspace.
+ */
+
+/**
+ * gstyle_hsv_copy:
+ * @self: a #GstyleHSV
+ *
+ * Makes a copy of a #GstyleHSV
+ *
+ * The result must be freed through gstyle_hsv_free().
+ *
+ * Returns: a newly allocated #GstyleHSV with the same content as @self.
+ *
+ */
+GstyleHSV *
+gstyle_hsv_copy (const GstyleHSV *self)
+{
+ return g_slice_dup (GstyleHSV, self);
+}
+
+/**
+ * gstyle_hsv_free:
+ * @self: a #GstyleHSV
+ *
+ * Frees a #GstyleHSV created with gstyle_hsv_copy().
+ *
+ */
+void
+gstyle_hsv_free (GstyleHSV *self)
+{
+ g_slice_free (GstyleHSV, self);
+}
diff --git a/contrib/gstyle/gstyle-hsv.h b/contrib/gstyle/gstyle-hsv.h
new file mode 100644
index 0000000..c93f542
--- /dev/null
+++ b/contrib/gstyle/gstyle-hsv.h
@@ -0,0 +1,45 @@
+/* gstyle-hsv.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_HSV_H
+#define GSTYLE_HSV_H
+
+#include <glib.h>
+
+#include "gstyle-types.h"
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_HSV (gstyle_hsv_get_type ())
+
+struct _GstyleHSV
+{
+ gdouble h;
+ gdouble s;
+ gdouble v;
+ gdouble alpha;
+};
+
+GType gstyle_hsv_get_type (void) G_GNUC_CONST;
+
+GstyleHSV *gstyle_hsv_copy (const GstyleHSV *self);
+void gstyle_hsv_free (GstyleHSV *self);
+
+G_END_DECLS
+
+#endif /* GSTYLE_HSV_H */
diff --git a/contrib/gstyle/gstyle-palette-widget.c b/contrib/gstyle/gstyle-palette-widget.c
new file mode 100644
index 0000000..4f621f2
--- /dev/null
+++ b/contrib/gstyle/gstyle-palette-widget.c
@@ -0,0 +1,1454 @@
+/* gstyle-palette-widget.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-palette-widget"
+
+#include <cairo/cairo.h>
+#include <glib/gi18n.h>
+#include <gdk/gdk.h>
+#include <math.h>
+
+#include "gstyle-css-provider.h"
+#include "gstyle-color.h"
+#include "gstyle-color-widget.h"
+#include "gstyle-utils.h"
+
+#include "gstyle-palette-widget.h"
+
+/*
+ * Each palette are refed twice, once when added to the #GListStore,
+ * once when they are binded to the display widget (#GtkListBox or #GtkFlowBox ).
+ * Both references are removed when the #GstylePalette is removed from the #GstylePaletteWidget.
+ */
+
+struct _GstylePaletteWidget
+{
+ GtkBin parent_instance;
+
+ GstyleCssProvider *default_provider;
+ GListStore *palettes;
+ GstylePalette *selected_palette;
+
+ GtkWidget *placeholder_box;
+ GtkWidget *placeholder;
+ GtkStack *view_stack;
+ GtkWidget *listbox;
+ GtkWidget *flowbox;
+
+ GstyleColor *dnd_color;
+ gint dnd_child_index;
+ GdkPoint dnd_last_pos;
+ guint dnd_last_time;
+ gdouble dnd_speed;
+ gboolean is_on_drag;
+
+ GstylePaletteWidgetViewMode view_mode;
+ GstylePaletteWidgetSortMode sort_mode;
+
+ guint current_has_load : 1;
+ guint current_has_save : 1;
+
+ guint dnd_draw_highlight : 1;
+ guint is_dnd_at_end : 1;
+};
+
+G_DEFINE_TYPE (GstylePaletteWidget, gstyle_palette_widget, GTK_TYPE_BIN)
+
+#define GSTYLE_DND_SPEED_THRESHOLD 50
+#define DND_INDEX_START (G_MININT)
+
+static guint unsaved_palette_count = 0;
+
+enum {
+ PROP_0,
+ PROP_PLACEHOLDER,
+ PROP_SELECTED_PALETTE_ID,
+ PROP_VIEW_MODE,
+ PROP_SORT_MODE,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+enum {
+ ACTIVATED,
+ PALETTE_ADDED,
+ PALETTE_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL];
+
+typedef struct _CursorInfo
+{
+ GstyleColorWidget *child;
+ gint index;
+ gint dest_x;
+ gint dest_y;
+ gint nb_col;
+} CursorInfo;
+
+static gint
+flowbox_get_nb_col (GstylePaletteWidget *self,
+ GtkFlowBox *flowbox)
+{
+ GtkFlowBoxChild *child;
+ GtkAllocation alloc;
+ guint min_per_line;
+ guint max_per_line;
+ gint previous_x = -1;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GTK_IS_FLOW_BOX (flowbox));
+ g_assert (gtk_flow_box_get_homogeneous (flowbox) == TRUE);
+
+ /* Child width is not perfectly constant, so we need some tricks */
+ min_per_line = gtk_flow_box_get_min_children_per_line (flowbox);
+ if (min_per_line < 1)
+ min_per_line = 1;
+
+ max_per_line = gtk_flow_box_get_max_children_per_line (flowbox);
+ if (max_per_line < 1)
+ max_per_line = 20;
+
+ for (guint i = 0; i <= max_per_line; ++i)
+ {
+ child = gtk_flow_box_get_child_at_index (flowbox, i);
+ if (child != NULL)
+ {
+ gtk_widget_get_allocation (GTK_WIDGET (child), &alloc);
+ if (alloc.x > previous_x)
+ {
+ previous_x = alloc.x;
+ continue;
+ }
+ }
+
+ return i;
+ }
+
+ return -1;
+}
+
+/* We make the assumption that the list is in order of apparition,
+ * in vertical orientation, with all children visible and
+ * homogenous set.
+ */
+static GtkFlowBoxChild *
+flowbox_get_child_at_xy (GstylePaletteWidget *self,
+ gint x,
+ gint y,
+ gint *index,
+ gint *nb_col)
+{
+ GtkFlowBox *flowbox;
+ GtkFlowBoxChild *child;
+ GtkAllocation alloc;
+ gint row_spacing;
+ guint right;
+ gint bottom;
+ guint index_y = 0;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (index != NULL);
+ g_assert (nb_col != NULL);
+
+ *index = -1;
+ if (x == -1 || y == -1)
+ return NULL;
+
+ flowbox = GTK_FLOW_BOX (self->flowbox);
+
+ g_assert (gtk_flow_box_get_homogeneous (flowbox) == TRUE);
+
+ child = gtk_flow_box_get_child_at_index (flowbox, 0);
+ if (child == NULL)
+ return NULL;
+
+ gtk_widget_get_allocation (GTK_WIDGET (child), &alloc);
+ row_spacing = gtk_flow_box_get_row_spacing (flowbox);
+
+ *nb_col = flowbox_get_nb_col (self, flowbox);
+ index_y = (y / (alloc.height + row_spacing) * (*nb_col));
+ for (guint i = index_y; i < (index_y + *nb_col); ++i)
+ {
+ child = gtk_flow_box_get_child_at_index (flowbox, i);
+ if (child != NULL)
+ {
+ gtk_widget_get_allocation (GTK_WIDGET (child), &alloc);
+ right = alloc.x + alloc.width;
+ bottom = alloc.y + alloc.height;
+ if (alloc.x <= x && x < right && alloc.y <= y && y < bottom)
+ {
+ *index = i;
+ return child;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* x,y are in #GstylePaletteWidget coordinates.
+ * if the cursor is on the placeholder, %FALSE is returned.
+ */
+static gboolean
+dnd_get_index_from_cursor (GstylePaletteWidget *self,
+ gint x,
+ gint y,
+ CursorInfo *info)
+{
+ GtkBin *bin_child;
+ gint len;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (info != NULL);
+
+ if (self->view_mode == GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST)
+ {
+ gtk_widget_translate_coordinates (GTK_WIDGET (self), self->listbox, x, y, &info->dest_x,
&info->dest_y);
+ bin_child = GTK_BIN (gtk_list_box_get_row_at_y (GTK_LIST_BOX (self->listbox), info->dest_y));
+ if (bin_child == NULL)
+ {
+ len = gstyle_palette_get_len (self->selected_palette);
+ if (len == 0)
+ return FALSE;
+
+ bin_child = GTK_BIN (gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->listbox), len - 1));
+ }
+
+ info->index = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (bin_child));
+ info->child = GSTYLE_COLOR_WIDGET (gtk_bin_get_child (GTK_BIN (bin_child)));
+ }
+ else
+ {
+ gtk_widget_translate_coordinates (GTK_WIDGET (self), self->flowbox, x, y, &info->dest_x,
&info->dest_y);
+ bin_child = GTK_BIN (flowbox_get_child_at_xy (self, info->dest_x, info->dest_y, &info->index,
&info->nb_col));
+ if (bin_child == NULL)
+ return FALSE;
+
+ info->child = GSTYLE_COLOR_WIDGET (gtk_bin_get_child (GTK_BIN (bin_child)));
+ }
+
+ return TRUE;
+}
+
+/* Returns: The insertion index
+ * x and y are in #GstylePaletteWidget coordinates.
+ * if either x or y are equal to -1, then the highlight
+ * is removed, and -1 is returned in this case.
+ */
+static gint
+dnd_highlight_set_from_cursor (GstylePaletteWidget *self,
+ gint x,
+ gint y)
+{
+ GtkAllocation alloc;
+ CursorInfo info;
+ gint dest_ref;
+ gint start;
+ gint size;
+ gboolean highlight;
+ gboolean redraw = FALSE;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+
+ if (x != -1 && y != -1 && dnd_get_index_from_cursor (self, x, y, &info))
+ {
+ gtk_widget_get_allocation (GTK_WIDGET (info.child), &alloc);
+ if (self->view_mode == GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST)
+ {
+ gint len;
+
+ dest_ref = info.dest_y;
+ start = alloc.y;
+ size = alloc.height;
+
+ len = gstyle_palette_get_len (self->selected_palette);
+ self->is_dnd_at_end = (info.index == (len - 1));
+ }
+ else
+ {
+ dest_ref = info.dest_x;
+ start = alloc.x;
+ size = alloc.width;
+
+ /* Check if we in the rightmost column */
+ self->is_dnd_at_end = ((info.index + 1)% info.nb_col == 0);
+ }
+
+ if (dest_ref > (start + size * 0.80))
+ info.index += 1;
+ else if (dest_ref > (start + size * 0.20))
+ info.index = -1;
+
+ highlight = TRUE;
+ }
+ else
+ {
+ highlight = FALSE;
+ info.index = -1;
+ }
+
+ if (self->dnd_draw_highlight != highlight || self->dnd_child_index != info.index)
+ redraw = TRUE;
+
+ self->dnd_child_index = info.index;
+ self->dnd_draw_highlight = highlight;
+
+ if (redraw)
+ {
+ if (self->view_mode == GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST)
+ gtk_widget_queue_draw (self->listbox);
+ else
+ gtk_widget_queue_draw (self->flowbox);
+ }
+
+ return info.index;
+}
+
+/* TODO: set the palette to need-attention if drop ( mean you need to save */
+/* TODO: take into account the case when the src and dst are from the same palette */
+/* TODO: do dnd from the flowbox */
+
+static gboolean
+gstyle_palette_widget_on_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GstylePaletteWidget *self = GSTYLE_PALETTE_WIDGET (widget);
+ GdkAtom target;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ target = gtk_drag_dest_find_target (widget, context, NULL);
+ if (target != gdk_atom_intern_static_string ("GSTYLE_COLOR_WIDGET"))
+ {
+ dnd_highlight_set_from_cursor (self, -1, -1);
+ gdk_drag_status (context, 0, time);
+ return FALSE;
+ }
+
+ dnd_highlight_set_from_cursor (self, x, y);
+ gdk_drag_status (context, GDK_ACTION_COPY, time);
+
+ return TRUE;
+}
+
+static void
+gstyle_palette_widget_on_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time)
+{
+ GstylePaletteWidget *self = GSTYLE_PALETTE_WIDGET (widget);
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ self->dnd_draw_highlight = FALSE;
+
+ if (self->view_mode == GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST)
+ gtk_widget_queue_draw (self->listbox);
+ else
+ gtk_widget_queue_draw (self->flowbox);
+}
+
+static gboolean
+gstyle_palette_widget_on_drag_drop (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GstylePaletteWidget *self = GSTYLE_PALETTE_WIDGET (widget);
+ GdkAtom target;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ target = gtk_drag_dest_find_target (widget, context, NULL);
+ if (target == gdk_atom_intern_static_string ("GSTYLE_COLOR_WIDGET"))
+ {
+ gtk_drag_get_data (widget, context, target, time);
+ return TRUE;
+ }
+
+ dnd_highlight_set_from_cursor (self, -1, -1);
+ return FALSE;
+}
+
+static void
+gstyle_palette_widget_on_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *data,
+ guint info,
+ guint time)
+{
+ GstylePaletteWidget *self = GSTYLE_PALETTE_WIDGET (widget);
+ GstyleColor **src_color;
+ GstyleColor *color;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GDK_IS_DRAG_CONTEXT (context));
+
+ if (gtk_selection_data_get_target (data) == gdk_atom_intern_static_string ("GSTYLE_COLOR_WIDGET"))
+ {
+ /* TODO: check if the color widget is coming from a PaletteWidget container */
+ src_color = (void*)gtk_selection_data_get_data (data);
+ color = gstyle_color_copy (*src_color);
+ gstyle_palette_add_at_index (self->selected_palette,
+ color,
+ self->dnd_child_index,
+ NULL);
+
+ g_object_unref (color);
+ gtk_drag_finish (context, TRUE, FALSE, time);
+ }
+ else
+ gtk_drag_finish (context, FALSE, FALSE, time);
+
+ dnd_highlight_set_from_cursor (self, -1, -1);
+}
+
+/**
+ * gstyle_palette_widget_set_placeholder:
+ * @self: A #GstylePaletteWidget.
+ * @placeholder: A #GtkWidget or %NULL.
+ *
+ * Set a placeholder to show when no palettes are loaded.
+ *
+ */
+void
+gstyle_palette_widget_set_placeholder (GstylePaletteWidget *self,
+ GtkWidget *placeholder)
+{
+ g_return_if_fail (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_return_if_fail (GTK_IS_WIDGET (self) || placeholder == NULL);
+
+ if (self->placeholder != placeholder)
+ {
+ if (self->placeholder != NULL)
+ gtk_container_remove (GTK_CONTAINER (self->placeholder_box), self->placeholder);
+
+ self->placeholder = placeholder;
+ if (placeholder != NULL)
+ {
+ gtk_container_add (GTK_CONTAINER (self->placeholder_box), placeholder);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PLACEHOLDER]);
+
+ if (self->selected_palette == NULL)
+ gtk_stack_set_visible_child_name (self->view_stack, "placeholder");
+ }
+ else
+ gstyle_palette_widget_set_view_mode (self, self->view_mode);
+ }
+}
+
+/**
+ * gstyle_palette_widget_get_placeholder:
+ * @self: A #GstylePaletteWidget.
+ *
+ * Get the current placeholder GtkWidget.
+ *
+ * Returns: (transfer none): A #GtkObject.
+ *
+ */
+GtkWidget *
+gstyle_palette_widget_get_placeholder (GstylePaletteWidget *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), NULL);
+
+ return self->placeholder;
+}
+
+static void
+gstyle_palette_widget_add_actions (GstylePaletteWidget *self)
+{
+ GSimpleActionGroup *actions_group;
+ GPropertyAction *action;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+
+ actions_group = g_simple_action_group_new ();
+
+ action = g_property_action_new ("view-mode", self, "view-mode");
+ g_action_map_add_action (G_ACTION_MAP (actions_group), G_ACTION (action));
+ action = g_property_action_new ("sort-mode", self, "sort-mode");
+ g_action_map_add_action (G_ACTION_MAP (actions_group), G_ACTION (action));
+
+ gtk_widget_insert_action_group (GTK_WIDGET (self), "gstyle-palettes-prefs", G_ACTION_GROUP
(actions_group));
+}
+
+static gint
+gstyle_palette_widget_get_palette_position (GstylePaletteWidget *self,
+ GstylePalette *palette)
+{
+ gint len;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GSTYLE_IS_PALETTE (palette));
+
+ len = g_list_model_get_n_items (G_LIST_MODEL (self->palettes));
+ for (gint n = 0; n < len; ++n)
+ {
+ g_autoptr (GstylePalette) model_palette = g_list_model_get_item (G_LIST_MODEL (self->palettes), n);
+
+ if (palette == model_palette)
+ return n;
+ }
+
+ return -1;
+}
+
+static GtkWidget *
+create_palette_list_item (gpointer item,
+ gpointer user_data)
+{
+ GstylePaletteWidget *self = (GstylePaletteWidget *)user_data;
+ GstyleColor *color = (GstyleColor *)item;
+ GtkWidget *row;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GSTYLE_IS_COLOR (color));
+
+ row = g_object_new (GSTYLE_TYPE_COLOR_WIDGET,
+ "color", color,
+ "visible", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ NULL);
+
+ return row;
+}
+
+static GtkWidget *
+create_palette_flow_item (gpointer item,
+ gpointer user_data)
+{
+ GstylePaletteWidget *self = (GstylePaletteWidget *)user_data;
+ GstyleColor *color = (GstyleColor *)item;
+ g_autofree gchar *color_string = NULL;
+ g_autofree gchar *tooltip = NULL;
+ const gchar *name;
+ GtkWidget *swatch;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GSTYLE_IS_COLOR (color));
+
+ name = gstyle_color_get_name (color);
+ if (gstyle_str_empty0 (name))
+ tooltip = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_ORIGINAL);
+ else
+ {
+ color_string = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_ORIGINAL);
+ tooltip = g_strdup_printf ("%s (%s)", name, color_string);
+ }
+
+ swatch = g_object_new (GSTYLE_TYPE_COLOR_WIDGET,
+ "color", color,
+ "visible", TRUE,
+ "name-visible", FALSE,
+ "fallback-name-visible", FALSE,
+ "tooltip-text", tooltip,
+ "width-request", 64,
+ "height-request", 64,
+ NULL);
+
+ return swatch;
+}
+
+static void
+bind_palette (GstylePaletteWidget *self,
+ GstylePalette *palette)
+{
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (palette == NULL || GSTYLE_IS_PALETTE (palette));
+ g_assert (palette == NULL || gstyle_palette_widget_get_palette_position (self, palette) != -1);
+
+ if (self->view_mode == GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST)
+ {
+ gtk_flow_box_bind_model (GTK_FLOW_BOX (self->flowbox), NULL, NULL, NULL, NULL);
+ if (palette != NULL)
+ {
+ gtk_list_box_bind_model (GTK_LIST_BOX (self->listbox), G_LIST_MODEL (palette),
+ create_palette_list_item, self, NULL);
+ gtk_stack_set_visible_child_name (self->view_stack, "list");
+ }
+ else
+ gtk_list_box_bind_model (GTK_LIST_BOX (self->listbox), NULL, NULL, NULL, NULL);
+ }
+ else
+ {
+ gtk_list_box_bind_model (GTK_LIST_BOX (self->listbox), NULL, NULL, NULL, NULL);
+ if (palette != NULL)
+ {
+ gtk_flow_box_bind_model (GTK_FLOW_BOX (self->flowbox), G_LIST_MODEL (palette),
+ create_palette_flow_item, self, NULL);
+ gtk_stack_set_visible_child_name (self->view_stack, "flow");
+ }
+ else
+ gtk_flow_box_bind_model (GTK_FLOW_BOX (self->flowbox), NULL, NULL, NULL, NULL);
+ }
+
+ self->selected_palette = palette;
+}
+
+static const gchar *
+gstyle_palette_widget_get_selected_palette_id (GstylePaletteWidget *self)
+{
+ GstylePalette *palette;
+
+ palette = gstyle_palette_widget_get_selected_palette (self);
+ if (palette != NULL)
+ return gstyle_palette_get_id (palette);
+ else
+ return "\0";
+}
+
+static void
+gstyle_palette_widget_set_selected_palette_by_id (GstylePaletteWidget *self,
+ const gchar *palette_id)
+{
+ const gchar *current_palette_id;
+ gint len;
+
+ len = g_list_model_get_n_items (G_LIST_MODEL (self->palettes));
+ for (gint n = 0; n < len; ++n)
+ {
+ g_autoptr (GstylePalette) palette = g_list_model_get_item (G_LIST_MODEL (self->palettes), n);
+
+ current_palette_id = gstyle_palette_get_id (palette);
+ if (g_strcmp0 (current_palette_id, palette_id) == 0)
+ gstyle_palette_widget_show_palette (self, palette);
+ }
+}
+
+/**
+ * gstyle_palette_widget_get_selected_palette:
+ * @self: A #GstylePaletteWidget
+ *
+ * Return the selected #GstylePalette.
+ *
+ * Returns: (transfer none) (allow-none): a #GstylePalette or %NULL if none are selected or loaded.
+ *
+ */
+GstylePalette *
+gstyle_palette_widget_get_selected_palette (GstylePaletteWidget *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), FALSE);
+
+ return self->selected_palette;
+}
+
+/**
+ * gstyle_palette_widget_show_palette:
+ * @self: A #GstylePaletteWidget
+ * @palette: A #GstylePalette
+ *
+ * Show @palette in the widget.
+ *
+ * Returns: %TRUE on succes, %FALSE if @palette is not in the list.
+ *
+ */
+gboolean
+gstyle_palette_widget_show_palette (GstylePaletteWidget *self,
+ GstylePalette *palette)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), FALSE);
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (palette), FALSE);
+
+ if (self->selected_palette != palette)
+ {
+ if (gstyle_palette_widget_get_palette_position (self, palette) == -1)
+ return FALSE;
+
+ bind_palette (self, palette);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTED_PALETTE_ID]);
+ }
+
+ return TRUE;
+}
+
+static void
+gstyle_palette_widget_color_swatch_activated (GstylePaletteWidget *self,
+ GtkFlowBoxChild *child,
+ GtkFlowBox *flowbox)
+{
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GTK_IS_FLOW_BOX (flowbox));
+ g_assert (GTK_IS_FLOW_BOX_CHILD (child));
+
+ g_signal_emit (self, signals [ACTIVATED], 0, self->selected_palette, gtk_flow_box_child_get_index (child));
+}
+
+static void
+gstyle_palette_widget_color_row_activated (GstylePaletteWidget *self,
+ GtkListBoxRow *row,
+ GtkListBox *listbox)
+{
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GTK_IS_LIST_BOX (listbox));
+ g_assert (GTK_IS_LIST_BOX_ROW (row));
+
+ g_signal_emit (self, signals [ACTIVATED], 0, self->selected_palette, gtk_list_box_row_get_index (row));
+}
+
+/**
+ * gstyle_palette_widget_get_n_palettes:
+ * @self: A #GstylePaletteWidget
+ *
+ * Get the number of #GstylePalette in the palettes list.
+ *
+ * Returns: number of palettes in the list.
+ *
+ */
+gint
+gstyle_palette_widget_get_n_palettes (GstylePaletteWidget *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), 0);
+
+ return g_list_model_get_n_items (G_LIST_MODEL (self->palettes));;
+}
+
+/**
+ * gstyle_palette_widget_get_palette_at_index:
+ * @self: A #GstylePaletteWidget
+ * @index: A position in the palette list, from 0 to (n - 1) palettes
+ *
+ * Get the #GstylePalette ref at position @index in the palettes list.
+ *
+ * Returns: (transfer none): A #GstylePalette or %NULL if index is out of bounds.
+ *
+ */
+GstylePalette *
+gstyle_palette_widget_get_palette_at_index (GstylePaletteWidget *self,
+ guint index)
+{
+ g_autoptr (GstylePalette) palette = NULL;
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), FALSE);
+
+ palette = g_list_model_get_item (G_LIST_MODEL (self->palettes), index);
+ return palette;
+}
+
+/**
+ * gstyle_palette_widget_get_store:
+ * @self: A #GstylePaletteWidget
+ *
+ * Return a #GListStore containing the palettes.
+ *
+ * Returns: (transfer none): #GListStore.
+ *
+ */
+GListStore *
+gstyle_palette_widget_get_store (GstylePaletteWidget *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), FALSE);
+
+ return self->palettes;
+}
+
+/**
+ * gstyle_palette_widget_get_list:
+ * @self: A #GstylePaletteWidget
+ *
+ * Return a #GList of the palettes.
+ *
+ * Returns: (transfer container) (element-type GstylePalette): #GList of #GstylePalette.
+ *
+ */
+GList *
+gstyle_palette_widget_get_list (GstylePaletteWidget *self)
+{
+ GList *l = NULL;
+ gint len;
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), FALSE);
+
+ len = g_list_model_get_n_items (G_LIST_MODEL (self->palettes));
+ if (len > 0)
+ for (gint n = (len - 1); n >= 0; --n)
+ {
+ g_autoptr (GstylePalette) palette = g_list_model_get_item (G_LIST_MODEL (self->palettes), n);
+
+ l = g_list_prepend (l, palette);
+ }
+
+ return l;
+}
+
+/**
+ * gstyle_palette_widget_add:
+ * @self: A #GstylePaletteWidget
+ * @palette: A #GstylePalette
+ *
+ * Add @palette to the widget list.
+ *
+ * Returns: %TRUE on succes, %FALSE if @palette is already in the list.
+ *
+ */
+gboolean
+gstyle_palette_widget_add (GstylePaletteWidget *self,
+ GstylePalette *palette)
+{
+ const gchar *id;
+ const gchar *list_id;
+ const gchar *palette_name;
+ gchar *name;
+ gint len;
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), FALSE);
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (palette), FALSE);
+
+ id = gstyle_palette_get_id (palette);
+ len = g_list_model_get_n_items (G_LIST_MODEL (self->palettes));
+ for (gint n = 0; n < len; ++n)
+ {
+ g_autoptr (GstylePalette) list_palette = g_list_model_get_item (G_LIST_MODEL (self->palettes), n);
+
+ list_id = gstyle_palette_get_id (list_palette);
+ if (g_strcmp0 (id, list_id) == 0)
+ return FALSE;
+ }
+
+ palette_name = gstyle_palette_get_name (palette);
+ if (gstyle_str_empty0 (palette_name))
+ {
+ name = g_strdup_printf ("Unsaved palette %u", unsaved_palette_count++);
+ gstyle_palette_set_name (palette, name);
+ g_free (name);
+ }
+
+ g_list_store_append (self->palettes, palette);
+ g_signal_emit (self, signals [PALETTE_ADDED], 0, palette);
+
+ return TRUE;
+}
+
+/**
+ * gstyle_palette_widget_remove_all:
+ * @self: A #GstylePaletteWidget
+ *
+ * Remove all palettes in the widget list.
+ *
+ */
+void
+gstyle_palette_widget_remove_all (GstylePaletteWidget *self)
+{
+ gint len;
+
+ g_return_if_fail (GSTYLE_IS_PALETTE_WIDGET (self));
+
+ len = g_list_model_get_n_items (G_LIST_MODEL (self->palettes));
+ for (gint n = 0; n < len; ++n)
+ {
+ g_autoptr (GstylePalette) palette = g_list_model_get_item (G_LIST_MODEL (self->palettes), n);
+
+ g_signal_emit (self, signals [PALETTE_REMOVED], 0, palette);
+ }
+
+ bind_palette (self, NULL);
+ g_list_store_remove_all (self->palettes);
+
+ gtk_stack_set_visible_child_name (self->view_stack, "placeholder");
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTED_PALETTE_ID]);
+}
+
+/**
+ * gstyle_palette_widget_remove:
+ * @self: A #GstylePaletteWidget
+ * @palette: A #GstylePalette
+ *
+ * Remove @palette in the widget list.
+ *
+ * Returns: %TRUE on succes, %FALSE if @palette is not in the list.
+ *
+ */
+gboolean
+gstyle_palette_widget_remove (GstylePaletteWidget *self,
+ GstylePalette *palette)
+{
+ gint len;
+ gint next;
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), FALSE);
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (palette), FALSE);
+
+ len = g_list_model_get_n_items (G_LIST_MODEL (self->palettes));
+ for (gint n = 0; n < len; ++n)
+ {
+ g_autoptr (GstylePalette) next_palette = NULL;
+ g_autoptr (GstylePalette) model_palette = g_list_model_get_item (G_LIST_MODEL (self->palettes), n);
+
+ if (palette == model_palette)
+ {
+ if (palette == self->selected_palette)
+ bind_palette (self, NULL);
+
+ g_list_store_remove (self->palettes, n);
+ g_signal_emit (self, signals [PALETTE_REMOVED], 0, palette);
+
+ len -= 1;
+ if (len > 0)
+ {
+ next = (n == len) ? (n - 1) : n;
+ next_palette = g_list_model_get_item (G_LIST_MODEL (self->palettes), next);
+ if (next_palette != NULL)
+ gstyle_palette_widget_show_palette (self, next_palette);
+ }
+ else
+ {
+ gtk_stack_set_visible_child_name (self->view_stack, "placeholder");
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTED_PALETTE_ID]);
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+
+}
+
+/**
+ * gstyle_palette_widget_get_palette_by_id:
+ * @self: A #GstylePaletteWidget
+ * @id: A palette id string
+ *
+ * Return the corresponding #GstylePalette if in the #GstylePaletteWidget list.
+ *
+ * Returns: (transfer none): A #GstylePalette if in the list, %NULL otherwise.
+ *
+ */
+GstylePalette *
+gstyle_palette_widget_get_palette_by_id (GstylePaletteWidget *self,
+ const gchar *id)
+{
+ const gchar *palette_id;
+ gint len;
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), NULL);
+ g_return_val_if_fail (!gstyle_str_empty0 (id), NULL);
+
+ len = g_list_model_get_n_items (G_LIST_MODEL (self->palettes));
+ for (gint n = 0; n < len; ++n)
+ {
+ g_autoptr (GstylePalette) palette = g_list_model_get_item (G_LIST_MODEL (self->palettes), n);
+
+ palette_id = gstyle_palette_get_id (palette);
+ if (g_strcmp0 (palette_id, id) == 0)
+ return palette;
+ }
+
+ return NULL;
+}
+
+/**
+ * gstyle_palette_widget_remove_by_id:
+ * @self: A #GstylePaletteWidget
+ * @id: A palette id string
+ *
+ * Remove palette with @id from the widget list.
+ *
+ * Returns: %TRUE on succes, %FALSE if not in the palettes list.
+ *
+ */
+gboolean
+gstyle_palette_widget_remove_by_id (GstylePaletteWidget *self,
+ const gchar *id)
+{
+ GstylePalette *palette;
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), FALSE);
+ g_return_val_if_fail (!gstyle_str_empty0 (id), FALSE);
+
+ palette = gstyle_palette_widget_get_palette_by_id (self, id);
+ if (palette != NULL)
+ {
+ gstyle_palette_widget_remove (self, palette);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gstyle_palette_widget_set_view_mode:
+ * @self: A #GstylePaletteWidget
+ * @mode: A #GstylePaletteWidgetViewMode
+ *
+ * Sets the view mode of the palette widget.
+ *
+ */
+void
+gstyle_palette_widget_set_view_mode (GstylePaletteWidget *self,
+ GstylePaletteWidgetViewMode mode)
+{
+ g_return_if_fail (GSTYLE_IS_PALETTE_WIDGET (self));
+
+ if (self->view_mode != mode)
+ {
+ self->view_mode = mode;
+ self->dnd_child_index = -1;
+ bind_palette (self, self->selected_palette);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VIEW_MODE]);
+ }
+
+
+ if (self->selected_palette != NULL || self->placeholder == NULL)
+ {
+ if (mode == GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST)
+ gtk_stack_set_visible_child_name (self->view_stack, "list");
+ else
+ gtk_stack_set_visible_child_name (self->view_stack, "flow");
+ }
+}
+
+/**
+ * gstyle_palette_widget_get_view_mode:
+ * @self: A #GstylePaletteWidget
+ *
+ * Get the view mode of the palette widget.
+ *
+ * Returns: The #GstylePaletteWidgetViewMode.
+ *
+ */
+GstylePaletteWidgetViewMode
+gstyle_palette_widget_get_view_mode (GstylePaletteWidget *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST);
+
+ return self->view_mode;
+}
+
+/**
+ * gstyle_palette_widget_set_sort_mode:
+ * @self: A #GstylePaletteWidget
+ * @mode: A #GstylePaletteWidgetViewMode
+ *
+ * Sets the sort mode of the palette widget.
+ *
+ */
+void
+gstyle_palette_widget_set_sort_mode (GstylePaletteWidget *self,
+ GstylePaletteWidgetSortMode mode)
+{
+ g_return_if_fail (GSTYLE_IS_PALETTE_WIDGET (self));
+
+ if (self->sort_mode != mode)
+ {
+ self->sort_mode = mode;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SORT_MODE]);
+ }
+}
+
+/**
+ * gstyle_palette_widget_get_sort_mode:
+ * @self: A #GstylePaletteWidget
+ *
+ * Get the sort mode of the palette widget.
+ *
+ * Returns: The #GstylePaletteWidgetSortMode.
+ *
+ */
+GstylePaletteWidgetSortMode
+gstyle_palette_widget_get_sort_mode (GstylePaletteWidget *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE_WIDGET (self), GSTYLE_PALETTE_WIDGET_SORT_MODE_ORIGINAL);
+
+ return self->sort_mode;
+}
+
+/**
+ * gstyle_palette_widget_new:
+ *
+ * Create a new #GstylePaletteWidget.
+ *
+ * Returns: A #GstylePaletteWidget.
+ *
+ */
+GstylePaletteWidget *
+gstyle_palette_widget_new (void)
+{
+ return g_object_new (GSTYLE_TYPE_PALETTE_WIDGET, NULL);
+}
+
+static gboolean
+listbox_draw_cb (GtkWidget *listbox,
+ cairo_t *cr,
+ GstylePaletteWidget *self)
+{
+ GtkStyleContext *style_context;
+ GtkListBoxRow *bin_child;
+ GtkAllocation alloc;
+ gint y;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GTK_IS_LIST_BOX (listbox));
+
+ if (self->dnd_draw_highlight && self->dnd_child_index != -1)
+ {
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_DND);
+
+ if (self->is_dnd_at_end)
+ {
+ bin_child = gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->listbox), self->dnd_child_index -
1);
+ gtk_widget_get_allocation (GTK_WIDGET (bin_child), &alloc);
+ y = alloc.y + alloc.height - 2;
+ }
+ else
+ {
+ bin_child = gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->listbox), self->dnd_child_index);
+ gtk_widget_get_allocation (GTK_WIDGET (bin_child), &alloc);
+ y = alloc.y - 2;
+ if (y < 0)
+ y = 0;
+ }
+
+ gtk_render_background (style_context, cr, alloc.x, y, alloc.width, 4);
+ gtk_render_frame (style_context, cr, alloc.x, y, alloc.width, 4);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+flowbox_draw_cb (GtkWidget *flowbox,
+ cairo_t *cr,
+ GstylePaletteWidget *self)
+{
+ GtkStyleContext *style_context;
+ GtkFlowBoxChild *bin_child;
+ GtkAllocation alloc;
+ gint len;
+ gint x;
+
+ g_assert (GSTYLE_IS_PALETTE_WIDGET (self));
+ g_assert (GTK_IS_FLOW_BOX (flowbox));
+
+ if (self->dnd_draw_highlight && self->dnd_child_index != -1)
+ {
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_DND);
+
+ len = gstyle_palette_get_len (self->selected_palette);
+ if (self->dnd_child_index == len || self->is_dnd_at_end)
+ {
+ bin_child = gtk_flow_box_get_child_at_index (GTK_FLOW_BOX (self->flowbox), self->dnd_child_index -
1);
+ gtk_widget_get_allocation (GTK_WIDGET (bin_child), &alloc);
+ x = alloc.x + alloc.width - 2;
+ }
+ else
+ {
+ bin_child = gtk_flow_box_get_child_at_index (GTK_FLOW_BOX (self->flowbox), self->dnd_child_index);
+ gtk_widget_get_allocation (GTK_WIDGET (bin_child), &alloc);
+ x = alloc.x - 2;
+ if (x < 0)
+ x = 0;
+ }
+
+ gtk_render_background (style_context, cr, x, alloc.y, 4, alloc.height);
+ gtk_render_frame (style_context, cr, x, alloc.y, 4, alloc.height);
+ }
+
+ return FALSE;
+}
+
+static void
+gstyle_palette_widget_finalize (GObject *object)
+{
+ GstylePaletteWidget *self = (GstylePaletteWidget *)object;
+
+ g_clear_object (&self->dnd_color);
+ g_clear_object (&self->placeholder);
+ g_clear_object (&self->default_provider);
+
+ bind_palette (self, NULL);
+ g_clear_object (&self->palettes);
+
+ G_OBJECT_CLASS (gstyle_palette_widget_parent_class)->finalize (object);
+}
+
+static void
+gstyle_palette_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstylePaletteWidget *self = GSTYLE_PALETTE_WIDGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_PLACEHOLDER:
+ g_value_set_object (value, gstyle_palette_widget_get_placeholder (self));
+ break;
+
+ case PROP_SELECTED_PALETTE_ID:
+ g_value_set_string (value, gstyle_palette_widget_get_selected_palette_id (self));
+ break;
+
+ case PROP_VIEW_MODE:
+ g_value_set_enum (value, gstyle_palette_widget_get_view_mode (self));
+ break;
+
+ case PROP_SORT_MODE:
+ g_value_set_enum (value, gstyle_palette_widget_get_sort_mode (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_palette_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstylePaletteWidget *self = GSTYLE_PALETTE_WIDGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_PLACEHOLDER:
+ gstyle_palette_widget_set_placeholder (self, g_value_get_object (value));
+ break;
+
+ case PROP_SELECTED_PALETTE_ID:
+ gstyle_palette_widget_set_selected_palette_by_id (self, g_value_get_string (value));
+ break;
+
+ case PROP_VIEW_MODE:
+ gstyle_palette_widget_set_view_mode (self, g_value_get_enum (value));
+ break;
+
+ case PROP_SORT_MODE:
+ gstyle_palette_widget_set_sort_mode (self, g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_palette_widget_class_init (GstylePaletteWidgetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gstyle_palette_widget_finalize;
+ object_class->get_property = gstyle_palette_widget_get_property;
+ object_class->set_property = gstyle_palette_widget_set_property;
+
+ widget_class->drag_motion = gstyle_palette_widget_on_drag_motion;
+ widget_class->drag_leave = gstyle_palette_widget_on_drag_leave;
+ widget_class->drag_drop = gstyle_palette_widget_on_drag_drop;
+ widget_class->drag_data_received = gstyle_palette_widget_on_drag_data_received;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libgstyle/ui/gstyle-palette-widget.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GstylePaletteWidget, view_stack);
+ gtk_widget_class_bind_template_child (widget_class, GstylePaletteWidget, placeholder_box);
+ gtk_widget_class_bind_template_child (widget_class, GstylePaletteWidget, listbox);
+ gtk_widget_class_bind_template_child (widget_class, GstylePaletteWidget, flowbox);
+
+ properties [PROP_PLACEHOLDER] =
+ g_param_spec_object ("placeholder",
+ "placeholder",
+ "placeholder GtkWidget",
+ GTK_TYPE_WIDGET,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SELECTED_PALETTE_ID] =
+ g_param_spec_string ("selected-palette-id",
+ "selected-palette-id",
+ "The selected palette id",
+ "",
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY |G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_VIEW_MODE] =
+ g_param_spec_enum ("view-mode",
+ "view-mode",
+ "The view mode of the palettes",
+ GSTYLE_TYPE_PALETTE_WIDGET_VIEW_MODE,
+ GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SORT_MODE] =
+ g_param_spec_enum ("sort-mode",
+ "sort-mode",
+ "The sort mode of the palettes",
+ GSTYLE_TYPE_PALETTE_WIDGET_SORT_MODE,
+ GSTYLE_PALETTE_WIDGET_SORT_MODE_ORIGINAL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ /**
+ * GstylePaletteWidget::activated:
+ * @self: A #GstylePaletteWidget
+ * @palette: a #GstylePalette
+ * @position: a position in the #palette
+ *
+ * This signal is emitted when you select a color in the palette widget.
+ */
+ signals [ACTIVATED] = g_signal_new ("activated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 2,
+ GSTYLE_TYPE_PALETTE,
+ G_TYPE_INT);
+
+ /**
+ * GstylePaletteWidget::palette-added:
+ * @self: A #GstylePaletteWidget
+ * @palette: a #GstylePalette
+ *
+ * This signal is emitted when a palette is added to the palette widget.
+ */
+ signals [PALETTE_ADDED] = g_signal_new ("palette-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ GSTYLE_TYPE_PALETTE);
+
+ /**
+ * GstylePaletteWidget::palette-removed:
+ * @self: A #GstylePaletteWidget
+ * @palette: a #GstylePalette
+ *
+ * This signal is emitted when a palette is removed to the palette widget.
+ */
+ signals [PALETTE_REMOVED] = g_signal_new ("palette-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ GSTYLE_TYPE_PALETTE);
+
+ gtk_widget_class_set_css_name (widget_class, "gstylepalettewidget");
+}
+
+static void
+gstyle_palette_widget_init (GstylePaletteWidget *self)
+{
+ GtkStyleContext *context;
+
+ static const GtkTargetEntry dnd_targets [] = {
+ {"GSTYLE_COLOR_WIDGET", GTK_TARGET_SAME_APP, 0},
+ };
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->view_mode = GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST;
+ gtk_stack_set_visible_child_name (self->view_stack, "list");
+
+ self->palettes = g_list_store_new (GSTYLE_TYPE_PALETTE);
+
+ gstyle_palette_widget_add_actions (self);
+
+ g_signal_connect_swapped (self->listbox,
+ "row-activated",
+ G_CALLBACK (gstyle_palette_widget_color_row_activated),
+ self);
+
+ g_signal_connect_after (self->listbox,
+ "draw",
+ G_CALLBACK (listbox_draw_cb),
+ self);
+
+ g_signal_connect_swapped (self->flowbox,
+ "child-activated",
+ G_CALLBACK (gstyle_palette_widget_color_swatch_activated),
+ self);
+
+ g_signal_connect_after (self->flowbox,
+ "draw",
+ G_CALLBACK (flowbox_draw_cb),
+ self);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ self->default_provider = gstyle_css_provider_init_default (gtk_style_context_get_screen (context));
+
+ gtk_drag_dest_set (GTK_WIDGET (self), 0,
+ dnd_targets, G_N_ELEMENTS (dnd_targets),
+ GDK_ACTION_COPY);
+
+ gtk_drag_dest_set_track_motion (GTK_WIDGET (self), TRUE);
+
+ self->dnd_color = gstyle_color_new ("placeholder", GSTYLE_COLOR_KIND_RGBA, 210, 210, 210, 100);
+ self->dnd_child_index = -1;
+}
+
+GType
+gstyle_palette_widget_view_mode_get_type (void)
+{
+ static GType view_mode_type_id;
+ static const GEnumValue values[] = {
+ { GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST, "GSTYLE_PALETTE_WIDGET__VIEW_MODE_LIST", "list" },
+ { GSTYLE_PALETTE_WIDGET_VIEW_MODE_SWATCHS, "GSTYLE_PALETTE_WIDGET__VIEW_MODE_SWATCHS", "swatchs" },
+ { 0 }
+ };
+
+ if (g_once_init_enter (&view_mode_type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_enum_register_static ("GstylePaletteWidgetViewMode", values);
+ g_once_init_leave (&view_mode_type_id, _type_id);
+ }
+
+ return view_mode_type_id;
+}
+
+GType
+gstyle_palette_widget_sort_mode_get_type (void)
+{
+ static GType sort_mode_type_id;
+ static const GEnumValue values[] = {
+ { GSTYLE_PALETTE_WIDGET_SORT_MODE_ORIGINAL, "GSTYLE_PALETTE_WIDGET_SORT_MODE_ORIGINAL", "original" },
+ { GSTYLE_PALETTE_WIDGET_SORT_MODE_LIGHT, "GSTYLE_PALETTE_WIDGET_SORT_MODE_LIGHT", "light" },
+ { GSTYLE_PALETTE_WIDGET_SORT_MODE_APPROCHING, "GSTYLE_PALETTE_WIDGET_SORT_MODE_APPROCHING", "approching"
},
+ { 0 }
+ };
+
+ if (g_once_init_enter (&sort_mode_type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_enum_register_static ("GstylePaletteWidgetSortMode", values);
+ g_once_init_leave (&sort_mode_type_id, _type_id);
+ }
+
+ return sort_mode_type_id;
+}
diff --git a/contrib/gstyle/gstyle-palette-widget.h b/contrib/gstyle/gstyle-palette-widget.h
new file mode 100644
index 0000000..177880b
--- /dev/null
+++ b/contrib/gstyle/gstyle-palette-widget.h
@@ -0,0 +1,83 @@
+/* gstyle-palette-widget.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_PALETTE_WIDGET_H
+#define GSTYLE_PALETTE_WIDGET_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-palette.h"
+#include "gstyle-types.h"
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_PALETTE_WIDGET (gstyle_palette_widget_get_type())
+#define GSTYLE_TYPE_PALETTE_WIDGET_VIEW_MODE (gstyle_palette_widget_view_mode_get_type())
+#define GSTYLE_TYPE_PALETTE_WIDGET_SORT_MODE (gstyle_palette_widget_sort_mode_get_type())
+
+G_DECLARE_FINAL_TYPE (GstylePaletteWidget, gstyle_palette_widget, GSTYLE, PALETTE_WIDGET, GtkBin)
+
+typedef enum
+{
+ GSTYLE_PALETTE_WIDGET_VIEW_MODE_LIST,
+ GSTYLE_PALETTE_WIDGET_VIEW_MODE_SWATCHS
+} GstylePaletteWidgetViewMode;
+
+typedef enum
+{
+ GSTYLE_PALETTE_WIDGET_SORT_MODE_ORIGINAL,
+ GSTYLE_PALETTE_WIDGET_SORT_MODE_LIGHT,
+ GSTYLE_PALETTE_WIDGET_SORT_MODE_APPROCHING
+} GstylePaletteWidgetSortMode;
+
+GType gstyle_palette_widget_view_mode_get_type (void);
+GType gstyle_palette_widget_sort_mode_get_type (void);
+
+gboolean gstyle_palette_widget_add (GstylePaletteWidget
*self,
+ GstylePalette
*palette);
+GList *gstyle_palette_widget_get_list (GstylePaletteWidget
*self);
+gint gstyle_palette_widget_get_n_palettes (GstylePaletteWidget
*self);
+GstylePalette *gstyle_palette_widget_get_palette_at_index (GstylePaletteWidget
*self,
+ guint
index);
+GstylePalette *gstyle_palette_widget_get_palette_by_id (GstylePaletteWidget
*self,
+ const gchar
*id);
+GtkWidget *gstyle_palette_widget_get_placeholder (GstylePaletteWidget
*self);
+GstylePalette *gstyle_palette_widget_get_selected_palette (GstylePaletteWidget
*self);
+GstylePaletteWidgetSortMode gstyle_palette_widget_get_sort_mode (GstylePaletteWidget
*self);
+GListStore *gstyle_palette_widget_get_store (GstylePaletteWidget
*self);
+GstylePaletteWidgetViewMode gstyle_palette_widget_get_view_mode (GstylePaletteWidget
*self);
+gboolean gstyle_palette_widget_remove (GstylePaletteWidget
*self,
+ GstylePalette
*palette);
+void gstyle_palette_widget_remove_all (GstylePaletteWidget
*self);
+gboolean gstyle_palette_widget_remove_by_id (GstylePaletteWidget
*self,
+ const gchar
*id);
+void gstyle_palette_widget_set_placeholder (GstylePaletteWidget
*self,
+ GtkWidget
*placeholder);
+void gstyle_palette_widget_set_sort_mode (GstylePaletteWidget
*self,
+ GstylePaletteWidgetSortMode
mode);
+void gstyle_palette_widget_set_view_mode (GstylePaletteWidget
*self,
+ GstylePaletteWidgetViewMode
mode);
+gboolean gstyle_palette_widget_show_palette (GstylePaletteWidget
*self,
+ GstylePalette
*palette);
+GstylePaletteWidget *gstyle_palette_widget_new (void);
+
+G_END_DECLS
+
+#endif /* GSTYLE_PALETTE_WIDGET_H */
+
diff --git a/contrib/gstyle/gstyle-palette.c b/contrib/gstyle/gstyle-palette.c
new file mode 100644
index 0000000..b25e973
--- /dev/null
+++ b/contrib/gstyle/gstyle-palette.c
@@ -0,0 +1,1134 @@
+/* gstyle-palette.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-palette"
+
+#include <glib/gi18n.h>
+#include <string.h>
+
+#include "gstyle-private.h"
+#include "gstyle-palette.h"
+
+#define XML_TO_CHAR(s) ((char *) (s))
+#define CHAR_TO_XML(s) ((unsigned char *) (s))
+
+#define GSTYLE_PALETTE_ID_CHARSET "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
+
+struct _GstylePalette
+{
+ GObject parent_instance;
+
+ GPtrArray *colors;
+ GHashTable *color_names;
+ gchar *id;
+ gchar *name;
+ GFile *file;
+};
+
+static void gstyle_palette_list_model_iface_init (GListModelInterface *iface);
+
+G_DEFINE_QUARK (gstyle_palette_error, gstyle_palette_error)
+
+G_DEFINE_TYPE_WITH_CODE (GstylePalette, gstyle_palette, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gstyle_palette_list_model_iface_init));
+
+enum {
+ PROP_0,
+ PROP_ID,
+ PROP_NAME,
+ PROP_FILE,
+ PROP_COLORS,
+ PROP_LEN,
+ N_PROPS
+};
+
+static inline gchar *
+strdup_and_xmlfree (xmlChar *xml_str)
+{
+ gchar *res = NULL;
+
+ if (xml_str != NULL)
+ {
+ res = g_strdup ((char *)xml_str);
+ xmlFree(xml_str);
+ }
+
+ return res;
+}
+
+static void
+gstyle_palette_error_cb (void *arg,
+ const char *msg,
+ xmlParserSeverities severity,
+ xmlTextReaderLocatorPtr locator)
+{
+ g_warning ("Parse error at line %i:\n%s\n",
+ xmlTextReaderLocatorLineNumber (locator),
+ msg);
+}
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+gstyle_palette_xml_get_header (xmlTextReaderPtr reader,
+ gchar **id,
+ gchar **name)
+{
+ g_assert (reader != NULL);
+ g_assert (id != NULL);
+ g_assert (name != NULL);
+
+ *id = *name = NULL;
+ if (xmlTextReaderRead(reader) == 1 &&
+ xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT &&
+ !g_strcmp0 (XML_TO_CHAR (xmlTextReaderConstName (reader)), "palette") &&
+ xmlTextReaderDepth (reader) == 0)
+ {
+ *id = strdup_and_xmlfree (xmlTextReaderGetAttribute (reader, CHAR_TO_XML ("id")));
+ *name = strdup_and_xmlfree (xmlTextReaderGetAttribute (reader, CHAR_TO_XML ("_name")));
+ if (gstyle_str_empty0 (*id) || gstyle_utf8_is_spaces (*id))
+ {
+ g_warning ("Palette '%s'has an empty or NULL id\n", *name);
+ return FALSE;
+ }
+
+ if (gstyle_utf8_is_spaces (*name))
+ g_clear_pointer (name, g_free);
+ }
+
+ return (*id != NULL);
+}
+
+static GstyleColor *
+gstyle_palette_xml_get_color (xmlTextReaderPtr reader)
+{
+ GstyleColor *color = NULL;
+ g_autofree gchar *name;
+ g_autofree gchar *value;
+
+ g_assert (reader != NULL);
+
+ if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT &&
+ !g_strcmp0 (XML_TO_CHAR (xmlTextReaderConstName (reader)), "color") &&
+ xmlTextReaderDepth (reader) == 1)
+ {
+ name = strdup_and_xmlfree (xmlTextReaderGetAttribute (reader, CHAR_TO_XML ("name")));
+ if (gstyle_utf8_is_spaces (name))
+ g_clear_pointer (&name, g_free);
+
+ value = strdup_and_xmlfree (xmlTextReaderGetAttribute (reader, CHAR_TO_XML ("value")));
+ if (!gstyle_str_empty0 (value))
+ color = gstyle_color_new_from_string (name, value);
+ }
+
+ return color;
+}
+
+/**
+ * gstyle_palette_get_colors:
+ * @self: a #GstylePalette
+ *
+ * Return an array of colors contained in the palette.
+ *
+ * Returns: (transfer none) (element-type GstyleColor): a #GPtrArray of #GstyleColor.
+ */
+GPtrArray *
+gstyle_palette_get_colors (GstylePalette *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), NULL);
+
+ return self->colors;
+}
+
+/**
+ * gstyle_palette_get_color_at_index:
+ * @self: a #GstylePalette
+ * @index: position of the #GstyleColor to get
+ *
+ * Return the #gstyleColor at @index position
+ * or %NULL if the index is out of bounds.
+ *
+ * Returns: (transfer none) (nullable): a #GstyleColor.
+ */
+const GstyleColor *
+gstyle_palette_get_color_at_index (GstylePalette *self,
+ guint index)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), NULL);
+ g_return_val_if_fail (index < self->colors->len, NULL);
+
+ return g_ptr_array_index (self->colors, index);
+}
+
+/* TODO: add an unnamed category ? */
+static gboolean
+add_color_to_names_sets (GstylePalette *self,
+ GstyleColor *color)
+{
+ const gchar *name;
+ GPtrArray *set;
+
+ g_assert (GSTYLE_IS_PALETTE (self));
+ g_assert (GSTYLE_IS_COLOR (color));
+
+ name = gstyle_color_get_name (color);
+ if (gstyle_str_empty0 (name))
+ return FALSE;
+
+ set = g_hash_table_lookup (self->color_names, name);
+ if (set == NULL)
+ {
+ set = g_ptr_array_new ();
+ g_hash_table_insert (self->color_names, (gpointer)name, set);
+ }
+
+ g_ptr_array_add (set, color);
+ return TRUE;
+}
+
+static gboolean
+remove_color_to_names_sets (GstylePalette *self,
+ GstyleColor *color)
+{
+ const gchar *name;
+ GPtrArray *set;
+ gboolean ret = FALSE;
+
+ g_assert (GSTYLE_IS_PALETTE (self));
+ g_assert (GSTYLE_IS_COLOR (color));
+
+ name = gstyle_color_get_name (color);
+ if (gstyle_str_empty0 (name))
+ return FALSE;
+
+ set = g_hash_table_lookup (self->color_names, name);
+ if (set == NULL)
+ return FALSE;
+
+ ret = g_ptr_array_remove (set, color);
+ if (set->len == 0)
+ {
+ g_ptr_array_unref (set);
+ g_hash_table_remove (self->color_names, name);
+ }
+
+ return ret;
+}
+
+/**
+ * gstyle_palette_add_at_index:
+ * @self: a #GstylePalette
+ * @color: A #GstyleColor
+ * @position: Position to insert the new color, from 0 to gstyle_palette_get_len() -1,
+ * or -1 to append it
+ * @error: (nullable): a #GError location or %NULL
+ *
+ * Add a #GstyleColor to the palette.
+ *
+ * Returns: %TRUE on succes, %FALSE otherwise.
+ */
+gboolean
+gstyle_palette_add_at_index (GstylePalette *self,
+ GstyleColor *color,
+ gint position,
+ GError **error)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), FALSE);
+ g_return_val_if_fail (GSTYLE_IS_COLOR (color), FALSE);
+
+ /* If we are just after the last position, we in fact do an append */
+ if (position == self->colors->len)
+ position = -1;
+
+ if (position == -1 ||
+ (position == 0 && self->colors->len == 0) ||
+ (0 <= position && position < self->colors->len))
+ {
+ g_object_ref (color);
+ g_ptr_array_insert (self->colors, position, color);
+ add_color_to_names_sets (self, color);
+
+ position = (position == -1) ? self->colors->len - 1 : position;
+ g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
+
+ return TRUE;
+ }
+ else
+ {
+ g_warning ("Color inserted in palette '%s' at out-of-bounds position %i in (0, %i)\n",
+ gstyle_palette_get_name (self),
+ position, self->colors->len - 1);
+
+ return FALSE;
+ }
+}
+
+/**
+ * gstyle_palette_add:
+ * @self: a #GstylePalette
+ * @color: A #GstyleColor
+ * @error: (nullable): a #GError location or %NULL
+ *
+ * Add a #GstyleColor to the palette.
+ *
+ * Returns: %TRUE on succes, %FALSE otherwise.
+ */
+gboolean
+gstyle_palette_add (GstylePalette *self,
+ GstyleColor *color,
+ GError **error)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), FALSE);
+ g_return_val_if_fail (GSTYLE_IS_COLOR (color), FALSE);
+
+ return gstyle_palette_add_at_index (self, color, -1, error);
+}
+
+/**
+ * gstyle_palette_remove_at_index:
+ * @self: a #GstylePalette
+ * @position: A position to remove the #GstyleColor from
+ *
+ * Try to remove the #GstyleColor at @position from the palette
+ *
+ * Returns: %TRUE on succes, %FALSE otherwise.
+ */
+gboolean
+gstyle_palette_remove_at_index (GstylePalette *self,
+ gint position)
+{
+ GstyleColor *color;
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), FALSE);
+
+ if (0 <= position && position < self->colors->len)
+ {
+ color = GSTYLE_COLOR (g_ptr_array_index (self->colors, position));
+ remove_color_to_names_sets (self, color);
+ g_ptr_array_remove_index (self->colors, position);
+ g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
+
+ return TRUE;
+ }
+ else
+ {
+ g_warning ("Trying to remove a Color in palette '%s' at out-of-bounds position %i in (0, %i)\n",
+ gstyle_palette_get_name (self),
+ position, self->colors->len - 1);
+
+ return FALSE;
+ }
+}
+
+/**
+ * gstyle_palette_remove:
+ * @self: a #GstylePalette
+ * @color: A #GstyleColor
+ *
+ * Try to remove a #GstyleColor from the palette.
+ *
+ * Returns: %TRUE on succes, %FALSE otherwise.
+ */
+gboolean
+gstyle_palette_remove (GstylePalette *self,
+ GstyleColor *color)
+{
+ GPtrArray *array;
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), FALSE);
+ g_return_val_if_fail (GSTYLE_IS_COLOR (self), FALSE);
+
+ array = self->colors;
+ for (gint i = 0; i < array->len; ++i)
+ {
+ if (array->pdata[i] == color)
+ {
+ remove_color_to_names_sets (self, color);
+ g_ptr_array_remove_index (array, i);
+ g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * gstyle_palette_lookup:
+ * @self: a #GstylePalette
+ * @name: A #GstyleColor name
+ *
+ * Search for one or several #GstyleColor named @name in the palette.
+ *
+ * Returns: (transfer none) (nullable) (element-type GstyleColor): a #GstyleColor pointer array.
+ */
+GPtrArray *
+gstyle_palette_lookup (GstylePalette *self,
+ const gchar *name)
+{
+ GPtrArray *set;
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), NULL);
+ g_return_val_if_fail (!gstyle_str_empty0 (name), NULL);
+
+ set = g_hash_table_lookup (self->color_names, name);
+
+ return set;
+}
+
+/**
+ * gstyle_palette_get_index:
+ * @self: a #GstylePalette
+ * @color: A #GstyleColor
+ *
+ * Search for a #GstyleColor in the palette and
+ * return its index or -1 if not found.
+ *
+ * Returns: An index in the palette or -1.
+ */
+gint
+gstyle_palette_get_index (GstylePalette *self,
+ GstyleColor *color)
+{
+ guint len;
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), -1);
+ g_return_val_if_fail (GSTYLE_COLOR (color), -1);
+
+ len = self->colors->len;
+ for (gint i = 0; i < len; i++)
+ if (color == g_ptr_array_index (self->colors, i))
+ return i;
+
+ return -1;
+}
+
+/* We skip commented lines and not prefixed by prefix if not NULL */
+static gchar *
+read_gpl_line (GDataInputStream *stream,
+ GError **error,
+ const gchar *prefix)
+{
+ gchar *line;
+
+ g_assert (G_IS_INPUT_STREAM (stream));
+
+ while ((line = g_data_input_stream_read_line_utf8 (stream, NULL, NULL, error)))
+ {
+ g_strchug (line);
+ if (*line == '#' || (prefix != NULL && !g_str_has_prefix (line, prefix)))
+ g_free (line);
+ else
+ break;
+ }
+
+ return line;
+}
+
+static gboolean
+read_gpl_header (GDataInputStream *stream,
+ gchar **palette_name,
+ gint *line_count,
+ GError **error)
+{
+ gchar *line;
+
+ g_assert (G_IS_INPUT_STREAM (stream));
+ g_assert (palette_name != NULL);
+ g_assert (line_count != NULL);
+
+ if ((line = read_gpl_line (stream, error, "GIMP Palette")))
+ {
+ g_free (line);
+ ++(*line_count);
+ line = read_gpl_line (stream, error, "Name:");
+ if (line != NULL && g_str_has_prefix (line, "Name:"))
+ {
+ *palette_name = g_strdup (g_strstrip (&line[5]));
+ if (gstyle_utf8_is_spaces (*palette_name))
+ g_clear_pointer (palette_name, g_free);
+
+ g_free (line);
+
+ return TRUE;
+ }
+ }
+
+ g_set_error (error, GSTYLE_PALETTE_ERROR, GSTYLE_PALETTE_ERROR_PARSE,
+ _("failed to parse line %i\n"), *line_count);
+
+ return FALSE;
+}
+
+static gboolean
+read_gpl_color_line (GDataInputStream *stream,
+ GdkRGBA *rgba,
+ gchar **name,
+ gint *line_count,
+ GError **error)
+{
+ gchar *line;
+ gchar *cursor;
+ gchar *end;
+ gint value;
+
+ g_assert (G_IS_INPUT_STREAM (stream));
+ g_assert (rgba != NULL);
+ g_assert (name != NULL);
+ g_assert (line_count != NULL);
+
+ ++(*line_count);
+ while ((line = read_gpl_line (stream, error, NULL)))
+ if (*line >= '0' && *line <= '9')
+ break;
+ else
+ {
+ g_free (line);
+ ++(*line_count);
+ }
+
+ if (line == NULL)
+ return FALSE;
+
+ cursor = line;
+
+ rgba->alpha = 1.0;
+ value = strtol (cursor, &end, 10);
+ if (end != cursor && value >= 0 && value <= 255)
+ {
+ rgba->red = value / 255.0;
+ cursor = end;
+ value = strtol (cursor, &end, 10);
+ if (end != cursor && value >= 0 && value <= 255)
+ {
+ rgba->green = value / 255.0;
+ cursor = end;
+ value = strtol (cursor, &end, 10);
+ if (end != cursor && value >= 0 && value <= 255)
+ {
+ rgba->blue = value / 255.0;
+ cursor = end;
+ cursor = g_strstrip (cursor);
+ if (*cursor != '\0')
+ *name = g_strdup (cursor);
+ else
+ *name = NULL;
+
+ g_free (line);
+ return TRUE;
+ }
+ }
+ }
+
+ g_set_error (error, GSTYLE_PALETTE_ERROR, GSTYLE_PALETTE_ERROR_PARSE,
+ _("failed to parse line %i\n"), *line_count);
+
+ g_free (line);
+ return FALSE;
+}
+
+static GstylePalette *
+gstyle_palette_new_from_gpl (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GDataInputStream) data_stream = NULL;
+ g_autoptr (GInputStream) stream = NULL;
+ GstylePalette *palette = NULL;
+ g_autofree gchar *palette_name = NULL;
+ g_autofree gchar *id = NULL;
+ GstyleColor *color;
+ gchar *color_name;
+ GdkRGBA rgba;
+ GError *tmp_error = NULL;
+ gint line_count = 1;
+ gboolean has_colors = FALSE;
+
+ g_assert (G_IS_FILE (file));
+
+ if ((stream = G_INPUT_STREAM (g_file_read (file, cancellable, &tmp_error))))
+ {
+ data_stream = g_data_input_stream_new (stream);
+ if (read_gpl_header (data_stream, &palette_name, &line_count, &tmp_error))
+ {
+ id = g_strcanon (g_strdup (palette_name), GSTYLE_PALETTE_ID_CHARSET, '_');
+ palette = g_object_new (GSTYLE_TYPE_PALETTE,
+ "id", id,
+ "name", palette_name,
+ "file", file,
+ NULL);
+
+ while (read_gpl_color_line (data_stream, &rgba, &color_name, &line_count, &tmp_error))
+ {
+ has_colors = TRUE;
+ color = gstyle_color_new_from_rgba (color_name, GSTYLE_COLOR_KIND_RGB_HEX6, &rgba);
+ gstyle_palette_add (palette, color, &tmp_error);
+ g_object_unref (color);
+ g_free (color_name);
+ if (tmp_error != NULL)
+ break;
+ }
+ }
+ }
+
+ if (tmp_error == NULL && !has_colors)
+ {
+ g_autofree gchar *uri = g_file_get_uri (file);
+
+ g_set_error (&tmp_error, GSTYLE_PALETTE_ERROR, GSTYLE_PALETTE_ERROR_EMPTY,
+ _("%s : palette is empty\n"), uri);
+ }
+
+ if (tmp_error != NULL)
+ {
+ g_clear_object (&palette);
+ g_propagate_error (error, tmp_error);
+ }
+
+ return palette;
+}
+
+static int
+gstyle_palette_io_read_cb (void *user_data,
+ char *buffer,
+ int len)
+{
+ g_assert (G_IS_INPUT_STREAM(user_data));
+ g_assert (buffer != NULL);
+
+ return g_input_stream_read ((GInputStream *)user_data, buffer, len, NULL, NULL);
+}
+
+static int
+gstyle_palette_io_close_cb (void *user_data)
+{
+ g_assert (G_IS_INPUT_STREAM(user_data));
+
+ return g_input_stream_close ((GInputStream *)user_data, NULL, NULL) ? 0 : -1;
+}
+
+static GstylePalette *
+gstyle_palette_new_from_xml (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GInputStream) stream = NULL;
+ g_autofree gchar *uri = NULL;
+ GstylePalette *palette = NULL;
+ xmlTextReaderPtr reader;
+ GError *tmp_error = NULL;
+ gboolean has_colors = FALSE;
+ gint ret = -1;
+
+ g_assert (G_IS_FILE (file));
+
+ uri = g_file_get_uri (file);
+
+ if (!(stream = G_INPUT_STREAM (g_file_read (file, cancellable, &tmp_error))))
+ goto finish;
+
+ reader = xmlReaderForIO (gstyle_palette_io_read_cb,
+ gstyle_palette_io_close_cb,
+ stream,
+ uri,
+ NULL,
+ XML_PARSE_RECOVER | XML_PARSE_NOBLANKS | XML_PARSE_COMPACT);
+
+ if (reader != NULL)
+ {
+ GstyleColor *color;
+ g_autofree gchar *id = NULL;
+ g_autofree gchar *name = NULL;
+
+ xmlTextReaderSetErrorHandler (reader, gstyle_palette_error_cb, NULL);
+
+ if (xmlTextReaderRead(reader) &&
+ gstyle_palette_xml_get_header (reader, &id, &name))
+ {
+ palette = g_object_new (GSTYLE_TYPE_PALETTE,
+ "id", id,
+ "name", name,
+ "file", file,
+ NULL);
+
+ ret = xmlTextReaderRead(reader);
+ while (ret == 1)
+ {
+ if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_END_ELEMENT)
+ {
+ ret = 0;
+ break;
+ }
+
+ /* TODO: better naming */
+ color = gstyle_palette_xml_get_color (reader);
+ if (color == NULL)
+ {
+ ret = -1;
+ break;
+ }
+
+ gstyle_palette_add (palette, color, &tmp_error);
+ g_object_unref (color);
+ has_colors = TRUE;
+
+ ret = xmlTextReaderRead(reader);
+ }
+ }
+
+ if (ret != 0 || !has_colors)
+ {
+ g_clear_object (&palette);
+ g_set_error (&tmp_error, GSTYLE_PALETTE_ERROR, GSTYLE_PALETTE_ERROR_PARSE,
+ _("%s : failed to parse\n"), uri);
+ }
+
+ xmlTextReaderClose(reader);
+ xmlFreeTextReader(reader);
+ }
+ else
+ g_set_error (&tmp_error, GSTYLE_PALETTE_ERROR, GSTYLE_PALETTE_ERROR_FILE,
+ _("Unable to open %s\n"), uri);
+
+finish:
+
+ if (tmp_error)
+ g_propagate_error (error, tmp_error);
+
+ return palette;
+}
+
+/**
+ * gstyle_palette_new_from_file:
+ * @file: a #GFile
+ * @cancellable: A #GCancellable
+ * @error: (nullable): a #GError location or %NULL
+ *
+ * Load a palette from an .xml or .gpl file.
+ *
+ * Returns: A #GstylePalette.
+ */
+GstylePalette *
+gstyle_palette_new_from_file (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GstylePalette *palette = NULL;
+ g_autofree gchar *uri = NULL;
+ GError *tmp_error = NULL;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ uri = g_file_get_uri (file);
+ if (g_str_has_suffix (uri, "xml"))
+ palette = gstyle_palette_new_from_xml (file, cancellable, &tmp_error);
+ else if (g_str_has_suffix (uri, "gpl"))
+ palette = gstyle_palette_new_from_gpl (file, cancellable, &tmp_error);
+ else
+ g_set_error (&tmp_error, GSTYLE_PALETTE_ERROR, GSTYLE_PALETTE_ERROR_FORMAT,
+ _("%s : This file format is not supported\n"), uri);
+
+ /* TODO: check for duplicated color names */
+
+ if (tmp_error)
+ g_propagate_error (error, tmp_error);
+
+ return palette;
+}
+
+/**
+ * gstyle_palette_save_to_xml:
+ * @self: a #GstylePalette
+ * @file: a #GFile
+ * @error: (nullable): a #GError location or %NULL
+ *
+ * Save a palette to Gnome-Builder .xml palette format.
+ *
+ * Returns: %TRUE if succesfull, %FALSE otherwise.
+ */
+gboolean
+gstyle_palette_save_to_xml (GstylePalette *self,
+ GFile *file,
+ GError **error)
+{
+ g_autofree gchar *file_path = NULL;
+ const gchar *id;
+ const gchar *name;
+ xmlDocPtr doc;
+ xmlNodePtr root_node;
+ xmlNodePtr palette_node;
+ xmlNodePtr color_node;
+ gint n_colors;
+ gint succes;
+
+ gchar *header = "Copyright (C) 2016 GNOME Builder Team at irc.gimp.net/#gnome-builder\n" \
+ "This program is free software: you can redistribute it and/or modify\n" \
+ "it under the terms of the GNU General Public License as published by\n" \
+ "the Free Software Foundation, either version 3 of the License, or\n" \
+ "(at your option) any later version.\n\n" \
+ "This program is distributed in the hope that it will be useful,\n" \
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" \
+ "GNU General Public License for more details.\n\n" \
+ "You should have received a copy of the GNU General Public License\n" \
+ "along with this program. If not, see <http://www.gnu.org/licenses/>\n";
+
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ doc = xmlNewDoc(CHAR_TO_XML ("1.0"));
+ root_node = xmlNewDocComment (doc, CHAR_TO_XML (header));
+ xmlDocSetRootElement(doc, root_node);
+ palette_node = xmlNewNode (NULL, CHAR_TO_XML ("palette"));
+ xmlAddSibling (root_node, palette_node);
+
+ id = gstyle_palette_get_id (self);
+ name = gstyle_palette_get_name (self);
+ xmlNewProp (palette_node, CHAR_TO_XML ("id"), CHAR_TO_XML (id));
+ xmlNewProp (palette_node, CHAR_TO_XML ("_name"), CHAR_TO_XML (name));
+
+ n_colors = gstyle_palette_get_len (self);
+ for (gint i = 0; i < n_colors; ++i)
+ {
+ const GstyleColor *color;
+ const gchar *color_name;
+ g_autofree gchar *color_string = NULL;
+
+ color = gstyle_palette_get_color_at_index (self, i);
+ color_name = gstyle_color_get_name ((GstyleColor *)color);
+
+ if (gstyle_color_get_kind ((GstyleColor *)color) == GSTYLE_COLOR_KIND_PREDEFINED)
+ color_string = gstyle_color_to_string ((GstyleColor *)color, GSTYLE_COLOR_KIND_RGB_HEX6);
+ else
+ color_string = gstyle_color_to_string ((GstyleColor *)color, GSTYLE_COLOR_KIND_ORIGINAL);
+
+ color_node = xmlNewChild (palette_node, NULL, CHAR_TO_XML ("color"), NULL);
+ xmlNewProp (color_node, CHAR_TO_XML ("name"), CHAR_TO_XML (color_name));
+ xmlNewProp (color_node, CHAR_TO_XML ("value"), CHAR_TO_XML (color_string));
+ }
+
+ file_path = g_file_get_path (file);
+ succes = xmlSaveFormatFileEnc (file_path, doc, "UTF-8", 1);
+ xmlFreeDoc(doc);
+
+ if (succes == -1)
+ {
+ g_set_error (error, GSTYLE_PALETTE_ERROR, GSTYLE_PALETTE_ERROR_FILE,
+ _("Unable to save %s\n"), file_path);
+
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+/**
+ * gstyle_palette_set_name:
+ * @self: a #GstylePalette
+ * @name: palette name
+ *
+ */
+void
+gstyle_palette_set_name (GstylePalette *self,
+ const gchar *name)
+{
+ g_return_if_fail (GSTYLE_IS_PALETTE (self));
+
+ if (g_strcmp0 (self->name, name) != 0)
+ {
+ g_free (self->name);
+ self->name = g_strdup (name);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]);
+ }
+}
+
+/**
+ * gstyle_palette_set_id:
+ * @self: a #GstylePalette
+ * @id: palette id
+ *
+ */
+void
+gstyle_palette_set_id (GstylePalette *self,
+ const gchar *id)
+{
+ gint64 num_id;
+
+ g_return_if_fail (GSTYLE_IS_PALETTE (self));
+
+ g_free (self->id);
+ if (gstyle_str_empty0 (id))
+ {
+ num_id = g_get_real_time ();
+ self->id = g_strdup_printf ("gb-cp-%lu", num_id);
+ }
+ else if (g_strcmp0 (self->id, id) != 0)
+ self->id = g_strdup (id);
+}
+
+/**
+ * gstyle_palette_get_name:
+ * @self: a #GstylePalette
+ *
+ * Return the name of the palette.
+ *
+ * Returns: The palette's name.
+ */
+const gchar *
+gstyle_palette_get_name (GstylePalette *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), NULL);
+
+ return self->name;
+}
+
+/**
+ * gstyle_palette_get_id:
+ * @self: a #GstylePalette
+ *
+ * Return the palette id.
+ *
+ *
+ * Returns: The palette id string.
+ */
+const gchar *
+gstyle_palette_get_id (GstylePalette *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), NULL);
+
+ return self->id;
+}
+
+/**
+ * gstyle_palette_get_file:
+ * @self: a #GstylePalette
+ *
+ * Return the #GFile used to create the palette.
+ *
+ *
+ * Returns: (transfer full): a #GFile.
+ */
+GFile *
+gstyle_palette_get_file (GstylePalette *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), NULL);
+
+ return self->file;
+}
+
+/**
+ * gstyle_palette_get_len:
+ * @self: a #GstylePalette
+ *
+ * Return The number of colors in the palette.
+ *
+ *
+ * Returns: Palette's length.
+ */
+guint
+gstyle_palette_get_len (GstylePalette *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_PALETTE (self), 0);
+
+ return self->colors->len;
+}
+
+GstylePalette *
+gstyle_palette_new (void)
+{
+ return g_object_new (GSTYLE_TYPE_PALETTE, NULL);
+}
+
+static void
+gstyle_palette_finalize (GObject *object)
+{
+ GstylePalette *self = GSTYLE_PALETTE (object);
+
+ g_ptr_array_unref (self->colors);
+ g_hash_table_unref (self->color_names);
+
+ g_free (self->name);
+ g_free (self->id);
+ g_clear_object (&self->file);
+
+ G_OBJECT_CLASS (gstyle_palette_parent_class)->finalize (object);
+}
+
+static void
+gstyle_palette_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstylePalette *self = GSTYLE_PALETTE (object);
+
+ switch (prop_id)
+ {
+ case PROP_ID:
+ g_value_set_string (value, self->id);
+ break;
+
+ case PROP_NAME:
+ g_value_set_string (value, self->name);
+ break;
+
+ case PROP_FILE:
+ g_value_set_object (value, self->file);
+ break;
+
+ case PROP_COLORS:
+ g_value_set_object (value, self->colors);
+ break;
+
+ case PROP_LEN:
+ g_value_set_uint (value, self->colors->len);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_palette_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstylePalette *self = GSTYLE_PALETTE (object);
+ GFile *file;
+
+ switch (prop_id)
+ {
+ case PROP_ID:
+ gstyle_palette_set_id (self, g_value_get_string (value));
+ break;
+
+ case PROP_NAME:
+ gstyle_palette_set_name (self, g_value_get_string (value));
+ break;
+
+ case PROP_FILE:
+ file = g_value_get_object (value);
+ self->file = file ? g_object_ref (file) : NULL;
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static GType
+gstyle_palette_list_model_get_item_type (GListModel *list)
+{
+ g_assert (GSTYLE_IS_PALETTE (list));
+
+ return GSTYLE_TYPE_PALETTE;
+}
+
+static guint
+gstyle_palette_list_model_get_n_items (GListModel *list)
+{
+ GstylePalette *self = (GstylePalette *)list;
+
+ g_assert (GSTYLE_IS_PALETTE (self));
+
+ return self->colors->len;
+}
+
+static gpointer
+gstyle_palette_list_model_get_item (GListModel *list,
+ guint position)
+{
+ GstylePalette *self = (GstylePalette *)list;
+
+ g_assert (GSTYLE_IS_PALETTE (self));
+
+ if (position < self->colors->len)
+ return g_object_ref (g_ptr_array_index (self->colors, position));
+ else
+ return NULL;
+}
+
+static void
+gstyle_palette_list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_item_type = gstyle_palette_list_model_get_item_type;
+ iface->get_n_items = gstyle_palette_list_model_get_n_items;
+ iface->get_item = gstyle_palette_list_model_get_item;
+}
+
+static void
+gstyle_palette_class_init (GstylePaletteClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gstyle_palette_finalize;
+ object_class->get_property = gstyle_palette_get_property;
+ object_class->set_property = gstyle_palette_set_property;
+
+ properties [PROP_ID] =
+ g_param_spec_string ("id",
+ "Palette id",
+ "The id of the palette.",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_NAME] =
+ g_param_spec_string ("name",
+ "Palette name",
+ "The palette name.",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_FILE] =
+ g_param_spec_object ("file",
+ "File",
+ "The uri, as a GFile, used to generate the palette.",
+ G_TYPE_FILE,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_COLORS] =
+ g_param_spec_object ("colors",
+ "Colors",
+ "An array of colors contained in the palette.",
+ G_TYPE_FILE,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_LEN] =
+ g_param_spec_uint ("len",
+ "Palette length",
+ "Palette length",
+ 0,
+ G_MAXUINT,
+ 0,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gstyle_palette_init (GstylePalette *self)
+{
+ self->colors = g_ptr_array_new_with_free_func (g_object_unref);
+ self->color_names = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify)g_ptr_array_unref);
+}
diff --git a/contrib/gstyle/gstyle-palette.h b/contrib/gstyle/gstyle-palette.h
new file mode 100644
index 0000000..aea4202
--- /dev/null
+++ b/contrib/gstyle/gstyle-palette.h
@@ -0,0 +1,86 @@
+/* gstyle-palette.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_PALETTE_H
+#define GSTYLE_PALETTE_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <libxml/xmlreader.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include "gstyle-color.h"
+
+G_BEGIN_DECLS
+
+#define GSTYLE_PALETTE_ERROR (gstyle_palette_error_quark())
+
+#define GSTYLE_TYPE_PALETTE (gstyle_palette_get_type())
+
+G_DECLARE_FINAL_TYPE (GstylePalette, gstyle_palette, GSTYLE, PALETTE, GObject)
+
+typedef enum
+{
+ GSTYLE_PALETTE_ERROR_DUP_COLOR_NAME,
+ GSTYLE_PALETTE_ERROR_EMPTY,
+ GSTYLE_PALETTE_ERROR_FILE,
+ GSTYLE_PALETTE_ERROR_FORMAT,
+ GSTYLE_PALETTE_ERROR_PARSE
+} GstylePaletteError;
+
+GQuark gstyle_palette_error_quark (void);
+
+GstylePalette *gstyle_palette_new (void);
+GstylePalette *gstyle_palette_new_from_file (GFile *file,
+ GCancellable *cancellable,
+ GError **error);
+gboolean gstyle_palette_add (GstylePalette *self,
+ GstyleColor *color,
+ GError **error);
+gboolean gstyle_palette_add_at_index (GstylePalette *self,
+ GstyleColor *color,
+ gint position,
+ GError **error);
+GPtrArray *gstyle_palette_get_colors (GstylePalette *self);
+const GstyleColor *gstyle_palette_get_color_at_index (GstylePalette *self,
+ guint index);
+gint gstyle_palette_get_index (GstylePalette *self,
+ GstyleColor *color);
+const gchar *gstyle_palette_get_id (GstylePalette *self);
+guint gstyle_palette_get_len (GstylePalette *self);
+const gchar *gstyle_palette_get_name (GstylePalette *self);
+GFile *gstyle_palette_get_file (GstylePalette *self);
+GPtrArray *gstyle_palette_lookup (GstylePalette *self,
+ const gchar *name);
+gboolean gstyle_palette_remove (GstylePalette *self,
+ GstyleColor *color);
+gboolean gstyle_palette_remove_at_index (GstylePalette *self,
+ gint position);
+gboolean gstyle_palette_save_to_xml (GstylePalette *self,
+ GFile *file,
+ GError **error);
+void gstyle_palette_set_name (GstylePalette *self,
+ const gchar *name);
+void gstyle_palette_set_id (GstylePalette *self,
+ const gchar *id);
+
+G_END_DECLS
+
+#endif /* GSTYLE_PALETTE_H */
diff --git a/contrib/gstyle/gstyle-private.h b/contrib/gstyle/gstyle-private.h
new file mode 100644
index 0000000..7a55e19
--- /dev/null
+++ b/contrib/gstyle/gstyle-private.h
@@ -0,0 +1,31 @@
+/* gstyle-private.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This file 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 file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_PRIVATE_H
+#define GSTYLE_PRIVATE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#include "gstyle-color-predefined.h"
+#include "gstyle-utils.h"
+
+G_END_DECLS
+
+#endif /* GSTYLE_PRIVATE_H */
diff --git a/contrib/gstyle/gstyle-revealer.c b/contrib/gstyle/gstyle-revealer.c
new file mode 100644
index 0000000..ce98feb
--- /dev/null
+++ b/contrib/gstyle/gstyle-revealer.c
@@ -0,0 +1,341 @@
+/* gstyle-revealer.c
+ *
+ * Copyright (C) 2016 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This revealer is meant to stay an internal one and deal with specific color panel problems.
+ * and it only contain code to handle DOWN direction :
+ * - specific handling of a GstyleColorWidget (containing a scrolled window)
+ * - duration coded in hard.
+ *
+ * although not perfect because we don't control the container and children position,
+ * it's very aceptable solution.
+ */
+
+#include "gstyle-animation.h"
+#include "gstyle-palette-widget.h"
+
+#include "gstyle-revealer.h"
+
+struct _GstyleRevealer
+{
+ GtkBin parent_instance;
+
+ GdkWindow *window;
+ gdouble duration;
+ gdouble offset;
+ gdouble src_offset;
+ gdouble dst_offset;
+ gulong animation_handler_id;
+ gint64 animation_starttime;
+ gdouble previous_height;
+ gint max_height;
+
+ guint revealed : 1;
+ guint duration_set : 1;
+ guint is_animating : 1;
+};
+
+/* TODO: use spped instead of duration */
+
+G_DEFINE_TYPE (GstyleRevealer, gstyle_revealer, GTK_TYPE_BIN)
+
+enum {
+ PROP_0,
+ N_PROPS
+};
+
+#define GSTYLE_REVEALER_DEFAULT_DURATION 500
+
+static void
+animate_stop (GstyleRevealer *self)
+{
+ g_assert (GSTYLE_IS_REVEALER (self));
+
+ if (self->animation_handler_id)
+ {
+ gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->animation_handler_id);
+ self->is_animating = FALSE;
+ self->animation_handler_id = 0;
+ }
+}
+
+static gboolean
+animation_tick_cb (GtkWidget *widget,
+ GdkFrameClock *frame_clock,
+ gpointer user_data)
+{
+ GstyleRevealer *self = (GstyleRevealer *)widget;
+ GtkWidget *child;
+ gint64 time;
+ gdouble time_offset;
+ gdouble ease_offset;
+
+ g_assert (GSTYLE_IS_REVEALER (self));
+ g_assert (frame_clock != NULL);
+
+ if (!self->is_animating)
+ return G_SOURCE_REMOVE;
+
+ time = gdk_frame_clock_get_frame_time (frame_clock);
+ time_offset = MIN ((time - self->animation_starttime) / ( 1000.0 * self->duration), 1.0);
+ ease_offset = gstyle_animation_ease_in_out_cubic (time_offset);
+ self->offset = ease_offset * (self->dst_offset - self->src_offset) + self->src_offset;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+
+ if (time_offset == 1.0)
+ {
+ animate_stop (self);
+ self->offset = self->dst_offset;
+ self->revealed = !!(self->offset);
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child != NULL)
+ gtk_widget_set_child_visible (child, self->revealed);
+
+ return G_SOURCE_REMOVE;
+ }
+ else
+ return G_SOURCE_CONTINUE;
+}
+
+void
+gstyle_revealer_set_reveal_child (GstyleRevealer *self,
+ gboolean reveal)
+{
+ GtkWidget *child;
+ GtkAllocation allocation;
+
+ g_return_if_fail (GSTYLE_IS_REVEALER (self));
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child == NULL || (!self->is_animating && reveal == self->revealed))
+ return;
+
+ animate_stop (self);
+
+ if (!self->duration_set)
+ self->duration = gstyle_animation_check_enable_animation () ? GSTYLE_REVEALER_DEFAULT_DURATION : 0;
+
+ if (reveal)
+ {
+ gtk_widget_get_allocated_size (GTK_WIDGET (self), &allocation, NULL);
+
+ self->src_offset = self->offset;
+ self->dst_offset = 1.0;
+ }
+ else
+ {
+ self->src_offset = self->offset;
+ self->dst_offset = 0.0;
+ }
+
+ if (GSTYLE_IS_PALETTE_WIDGET (child))
+ self->max_height = allocation.height;
+ else
+ self->max_height = G_MAXINT;
+
+ gtk_widget_set_child_visible (child, TRUE);
+
+ if (self->duration == 0)
+ {
+ animate_stop (self);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ }
+ else if (!self->animation_handler_id)
+ {
+ self->animation_starttime = g_get_monotonic_time();
+ self->animation_handler_id = gtk_widget_add_tick_callback (GTK_WIDGET (self), animation_tick_cb, self,
NULL);
+ self->is_animating = TRUE;
+ }
+}
+
+static void
+gstyle_revealer_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GstyleRevealer *self = (GstyleRevealer *)widget;
+ GtkRequisition min_child_req, nat_child_req;
+ GtkAllocation child_allocation;
+ GtkWidget *child;
+
+ g_assert (GSTYLE_IS_REVEALER (self));
+ g_assert (allocation != NULL);
+
+ gtk_widget_set_allocation (widget, allocation);
+ if (gtk_widget_get_realized (GTK_WIDGET (self)))
+ gdk_window_move_resize (self->window,
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child == NULL)
+ return;
+
+ if (!gtk_widget_get_visible (child))
+ return;
+
+ gtk_widget_get_preferred_size (child, &min_child_req, &nat_child_req);
+
+ child_allocation.width = allocation->width;
+ child_allocation.height = MAX (min_child_req.height, allocation->height);
+ child_allocation.x = 0;
+
+ if (GSTYLE_IS_PALETTE_WIDGET (child))
+ {
+ if (nat_child_req.height > allocation->height)
+ child_allocation.y = allocation->height * (self->offset - 1.0);
+ else
+ child_allocation.y = nat_child_req.height * (self->offset - 1.0);
+ }
+ else
+ child_allocation.y = 0;
+
+ gtk_widget_size_allocate (child, &child_allocation);
+
+ allocation->y = 0;
+ gtk_widget_set_clip (child, allocation);
+}
+
+GstyleRevealer *
+gstyle_revealer_new (void)
+{
+ return g_object_new (GSTYLE_TYPE_REVEALER, NULL);
+}
+
+static void
+gstyle_revealer_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (gstyle_revealer_parent_class)->finalize (object);
+}
+
+static void
+gstyle_revealer_get_preferred_width (GtkWidget *widget,
+ gint *min_width,
+ gint *nat_width)
+{
+ GstyleRevealer *self = (GstyleRevealer *)widget;
+ GtkWidget *child;
+
+ g_assert (GSTYLE_IS_REVEALER (self));
+
+ *min_width = *nat_width = 1;
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child != NULL)
+ gtk_widget_get_preferred_width (child, min_width, nat_width);
+}
+
+static void
+gstyle_revealer_get_preferred_height (GtkWidget *widget,
+ gint *min_height,
+ gint *nat_height)
+{
+ GstyleRevealer *self = (GstyleRevealer *)widget;
+ gint child_min_height;
+ gint child_nat_height;
+
+ g_assert (GSTYLE_IS_REVEALER (self));
+
+ GTK_WIDGET_CLASS (gstyle_revealer_parent_class)->get_preferred_height (widget, &child_min_height,
&child_nat_height);
+
+ *min_height = 0;
+ *nat_height = MIN (self->max_height, child_nat_height) * self->offset;
+}
+
+static void
+gstyle_revealer_get_preferred_height_for_width (GtkWidget *widget,
+ gint width,
+ gint *min_height,
+ gint *nat_height)
+{
+ gstyle_revealer_get_preferred_height (widget, min_height, nat_height);
+}
+
+static void
+gstyle_revealer_realize (GtkWidget *widget)
+{
+ GstyleRevealer *self = (GstyleRevealer *)widget;
+ GtkAllocation alloc;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ g_assert (GSTYLE_IS_REVEALER (self));
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.width = alloc.width;
+ attributes.height = alloc.height;
+ attributes.x = alloc.x;
+ attributes.y = alloc.y;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+ attributes.event_mask = gtk_widget_get_events (widget);
+
+ self->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
+ gtk_widget_register_window (widget, self->window);
+ gtk_widget_set_window (GTK_WIDGET (self), self->window);
+
+ gtk_widget_set_realized (widget, TRUE);
+}
+
+static void
+gstyle_revealer_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ GstyleRevealer *self = (GstyleRevealer *)container;
+
+ g_assert (GSTYLE_IS_REVEALER (self));
+
+ gtk_widget_set_parent_window (child, self->window);
+ gtk_widget_set_child_visible (child, self->revealed);
+
+ GTK_CONTAINER_CLASS (gstyle_revealer_parent_class)->add (container, child);
+}
+
+static void
+gstyle_revealer_class_init (GstyleRevealerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gstyle_revealer_finalize;
+
+ container_class->add = gstyle_revealer_add;
+
+ widget_class->size_allocate = gstyle_revealer_size_allocate;
+ widget_class->get_preferred_width = gstyle_revealer_get_preferred_width;
+ widget_class->get_preferred_height = gstyle_revealer_get_preferred_height;
+ widget_class->get_preferred_height_for_width = gstyle_revealer_get_preferred_height_for_width;
+ widget_class->realize = gstyle_revealer_realize;
+}
+
+static void
+gstyle_revealer_init (GstyleRevealer *self)
+{
+ gtk_widget_set_has_window (GTK_WIDGET (self), TRUE);
+
+ self->duration = 500.0;
+ self->duration_set = TRUE;
+ self->max_height = G_MAXINT;
+}
diff --git a/contrib/gstyle/gstyle-revealer.h b/contrib/gstyle/gstyle-revealer.h
new file mode 100644
index 0000000..b159873
--- /dev/null
+++ b/contrib/gstyle/gstyle-revealer.h
@@ -0,0 +1,37 @@
+/* gstyle-revealer.h
+ *
+ * Copyright (C) 2016 Sebastien Lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_REVEALER_H
+#define GSTYLE_REVEALER_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_REVEALER (gstyle_revealer_get_type())
+
+G_DECLARE_FINAL_TYPE (GstyleRevealer, gstyle_revealer, GSTYLE, REVEALER, GtkBin)
+
+GstyleRevealer *gstyle_revealer_new (void);
+void gstyle_revealer_set_reveal_child (GstyleRevealer *self,
+ gboolean reveal);
+
+G_END_DECLS
+
+#endif /* GSTYLE_REVEALER_H */
diff --git a/contrib/gstyle/gstyle-slidein.c b/contrib/gstyle/gstyle-slidein.c
new file mode 100644
index 0000000..c79e73a
--- /dev/null
+++ b/contrib/gstyle/gstyle-slidein.c
@@ -0,0 +1,1328 @@
+/* gstyle-slidein.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Initial ideas based on Gnome Builder Pnl dock system :
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ */
+
+#define G_LOG_DOMAIN "gstyle-slidein"
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gdk/gdk.h>
+
+#include "gstyle-animation.h"
+#include "gstyle-css-provider.h"
+#include "gstyle-utils.h"
+
+#include "gstyle-slidein.h"
+
+struct _GstyleSlidein
+{
+ GtkEventBox parent_instance;
+
+ GstyleCssProvider *default_provider;
+ GtkWidget *overlay_child;
+ GdkWindow *overlay_window;
+
+ gint64 animation_starttime;
+ gdouble offset;
+ gdouble src_offset;
+ gdouble dst_offset;
+ gdouble slide_fraction;
+ gdouble duration;
+ guint slide_margin;
+
+ gulong revealed_handler_id;
+ gulong animation_handler_id;
+
+ GstyleSlideinDirectionType direction_type : 3;
+ GstyleSlideinDirectionType direction_type_reverse : 3;
+ GstyleSlideinDirectionType real_direction;
+ guint interpolate_size : 1;
+ guint revealed : 1;
+ guint transition_done : 1;
+ guint duration_set : 1;
+ guint is_opening : 1;
+ guint is_closing : 1;
+};
+
+static void gstyle_slidein_init_buildable_iface (GtkBuildableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GstyleSlidein, gstyle_slidein, GTK_TYPE_EVENT_BOX, 0,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, gstyle_slidein_init_buildable_iface))
+
+enum {
+ PROP_0,
+ PROP_DIRECTION_TYPE,
+ PROP_DURATION,
+ PROP_INTERPOLATE_SIZE,
+ PROP_SLIDE_FRACTION,
+ PROP_SLIDE_MARGIN,
+ PROP_REVEALED,
+ N_PROPS
+};
+
+enum {
+ CLOSING,
+ OPENING,
+ REVEALED,
+ N_SIGNALS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static guint signals[N_SIGNALS];
+
+static inline GtkOrientation
+get_orientation (GstyleSlidein *self)
+{
+ if (self->direction_type == GSTYLE_SLIDEIN_DIRECTION_TYPE_UP ||
+ self->direction_type == GSTYLE_SLIDEIN_DIRECTION_TYPE_DOWN)
+ return GTK_ORIENTATION_VERTICAL;
+ else
+ return GTK_ORIENTATION_HORIZONTAL;
+}
+
+GstyleSlideinDirectionType
+gstyle_slidein_get_direction_type (GstyleSlidein *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_SLIDEIN (self), GSTYLE_SLIDEIN_DIRECTION_TYPE_NONE);
+
+ return self->direction_type;
+}
+
+/**
+ * gstyle_slidein_set_direction_type:
+ * @direction_type: a #GstyleSlideinDirectionType
+ *
+ * Set the type of animation direction.
+ *
+ */
+void
+gstyle_slidein_set_direction_type (GstyleSlidein *self,
+ GstyleSlideinDirectionType direction_type)
+{
+ g_return_if_fail (GSTYLE_IS_SLIDEIN (self));
+
+ if (self->direction_type != direction_type)
+ {
+ self->direction_type = direction_type;
+
+ if (direction_type == GSTYLE_SLIDEIN_DIRECTION_TYPE_LEFT)
+ self->direction_type_reverse = GSTYLE_SLIDEIN_DIRECTION_TYPE_RIGHT;
+ else if (direction_type == GSTYLE_SLIDEIN_DIRECTION_TYPE_RIGHT)
+ self->direction_type_reverse = GSTYLE_SLIDEIN_DIRECTION_TYPE_LEFT;
+ else if (direction_type == GSTYLE_SLIDEIN_DIRECTION_TYPE_UP)
+ self->direction_type_reverse = GSTYLE_SLIDEIN_DIRECTION_TYPE_DOWN;
+ else if (direction_type == GSTYLE_SLIDEIN_DIRECTION_TYPE_DOWN)
+ self->direction_type_reverse = GSTYLE_SLIDEIN_DIRECTION_TYPE_UP;
+ else if (direction_type == GSTYLE_SLIDEIN_DIRECTION_TYPE_NONE)
+ self->direction_type_reverse = GSTYLE_SLIDEIN_DIRECTION_TYPE_NONE;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DIRECTION_TYPE]);
+ }
+}
+
+/**
+ * gstyle_slidein_set_interpolate_size:
+ * @interpolate_size: %TRUE to interpolate the size
+ *
+ * Set whether or not we size the slidein according to all its children
+ * (regular and slide) or just the regular one.
+ *
+ */
+void
+gstyle_slidein_set_interpolate_size (GstyleSlidein *self,
+ gboolean interpolate_size)
+{
+ g_return_if_fail (GSTYLE_IS_SLIDEIN (self));
+
+ if (self->interpolate_size != interpolate_size)
+ {
+ self->interpolate_size = interpolate_size;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_INTERPOLATE_SIZE]);
+ }
+}
+
+/**
+ * gstyle_slidein_get_interpolate_size:
+ *
+ * Get the interpolate-size set by gstyle_slidein_set_interpolate_size().
+ *
+ * Returns: %TRUE if we interpolate the size, %FALSE otherwise.
+ */
+gboolean
+gstyle_slidein_get_interpolate_size (GstyleSlidein *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_SLIDEIN (self), FALSE);
+
+ return self->interpolate_size;
+}
+
+/**
+ * gstyle_slidein_set_slide_fraction:
+ * @slide_fraction: fraction of the slide compared to the total size
+ *
+ * Set the fraction used by the slide compared to the total size of the slidein,
+ * in the direction of the animation (so horizontal or vertical).
+ *
+ */
+void
+gstyle_slidein_set_slide_fraction (GstyleSlidein *self,
+ gdouble slide_fraction)
+{
+ g_return_if_fail (GSTYLE_IS_SLIDEIN (self));
+
+ if (slide_fraction != self->slide_fraction)
+ {
+ self->slide_fraction = slide_fraction;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SLIDE_FRACTION]);
+ }
+}
+
+/**
+ * gstyle_slidein_get_slide_fraction:
+ *
+ * Get the slide_fraction set by gstyle_slidein_set_slide_fraction().
+ *
+ * Returns: the slide_fraction.
+ */
+gdouble
+gstyle_slidein_get_slide_fraction (GstyleSlidein *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_SLIDEIN (self), 0);
+
+ return self->slide_fraction;
+}
+
+/**
+ * gstyle_slidein_set_slide_margin:
+ * @slide_margin: margin.
+ *
+ * Set the margin left when the slide is opened, in pixels.
+ *
+ */
+void
+gstyle_slidein_set_slide_margin (GstyleSlidein *self,
+ guint slide_margin)
+{
+ g_return_if_fail (GSTYLE_IS_SLIDEIN (self));
+
+ if (slide_margin != self->slide_margin)
+ {
+ self->slide_margin = slide_margin;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SLIDE_MARGIN]);
+ }
+}
+
+/**
+ * gstyle_slidein_get_slide_margin:
+ *
+ * Get the slide_margin set by gstyle_slidein_set_slide_margin().
+ *
+ * Returns: the slide_margin.
+ */
+guint
+gstyle_slidein_get_slide_margin (GstyleSlidein *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_SLIDEIN (self), 0);
+
+ return self->slide_margin;
+}
+
+/**
+ * gstyle_slidein_reset_duration:
+ *
+ * Reset the time taken to animate the slide to its default value.
+ *
+ */
+void
+gstyle_slidein_reset_duration (GstyleSlidein *self)
+{
+ g_return_if_fail (GSTYLE_IS_SLIDEIN (self));
+
+ self->duration = 0;
+ self->duration_set = FALSE;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]);
+}
+
+/**
+ * gstyle_slidein_set_duration:
+ * @duration: a time in ms
+ *
+ * Set the time taken to animation the slide to the custom @duration value, in ms.
+ *
+ */
+void
+gstyle_slidein_set_duration (GstyleSlidein *self,
+ gdouble duration)
+{
+ g_return_if_fail (GSTYLE_IS_SLIDEIN (self));
+
+ self->duration = duration;
+ self->duration_set = TRUE;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]);
+}
+
+/**
+ * gstyle_slidein_get_duration:
+ *
+ * Get the time set to animate the slide.
+ *
+ * Returns: a time in ms.
+ */
+gdouble
+gstyle_slidein_get_duration (GstyleSlidein *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_SLIDEIN (self), 0.0);
+
+ return self->duration;
+}
+
+static gdouble
+compute_duration (GstyleSlidein *self)
+{
+ GtkWidget *child;
+ GtkRequisition min_req_size;
+ GtkRequisition nat_req_size;
+ gdouble duration = 0.0;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ gtk_widget_get_preferred_size (child, &min_req_size, &nat_req_size);
+
+ if (get_orientation (self) == GTK_ORIENTATION_HORIZONTAL)
+ duration = MAX (300, (nat_req_size.width - self->slide_margin) * self->slide_fraction * 1.2);
+ else
+ duration = MAX (300, (nat_req_size.height - self->slide_margin) * self->slide_fraction * 1.2);
+
+ return duration;
+}
+
+static void
+animate_stop (GstyleSlidein *self)
+{
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ if (self->animation_handler_id)
+ {
+ gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->animation_handler_id);
+ self->is_closing = self->is_opening = FALSE;
+ self->animation_handler_id = 0;
+ }
+}
+
+static void
+animation_done_cb (GstyleSlidein *self)
+{
+ GstyleSlideinDirectionType animation_direction;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ if (self->is_opening)
+ {
+ animate_stop (self);
+ animation_direction = self->direction_type;
+ self->revealed = TRUE;
+
+ gtk_grab_add (GTK_WIDGET (self));
+ gtk_event_box_set_above_child (GTK_EVENT_BOX (self), TRUE);
+ gtk_widget_set_can_focus (self->overlay_child, TRUE);
+ gtk_widget_grab_focus (self->overlay_child);
+ }
+ else if (self->is_closing)
+ {
+ animate_stop (self);
+ animation_direction = self->direction_type_reverse;
+ self->revealed = FALSE;
+
+ gtk_event_box_set_above_child (GTK_EVENT_BOX (self), FALSE);
+ }
+ else
+ g_assert_not_reached ();
+
+ self->is_closing = self->is_opening = FALSE;
+ self->offset = self->dst_offset;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_REVEALED]);
+ g_signal_emit (self, signals [REVEALED], 0, animation_direction, self->revealed);
+}
+
+static gboolean
+animation_tick_cb (GtkWidget *widget,
+ GdkFrameClock *frame_clock,
+ gpointer user_data)
+{
+ GstyleSlidein *self = (GstyleSlidein *)widget;
+ gint64 time;
+ gdouble time_offset;
+ gdouble ease_offset;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+ g_assert (frame_clock != NULL);
+
+ if (!self->is_closing && !self->is_opening)
+ return G_SOURCE_REMOVE;
+
+ time = gdk_frame_clock_get_frame_time (frame_clock);
+ time_offset = MIN ((time - self->animation_starttime) / ( 1000.0 * self->duration), 1.0);
+ ease_offset = gstyle_animation_ease_in_out_cubic (time_offset);
+ self->offset = ease_offset * (self->dst_offset - self->src_offset) + self->src_offset;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+
+ if (time_offset == 1.0)
+ {
+ animation_done_cb (self);
+ return G_SOURCE_REMOVE;
+ }
+ else
+ return G_SOURCE_CONTINUE;
+}
+
+/* src_offset and dst_offset need to be in [0.0, 1.0] range */
+static gboolean
+animate (GstyleSlidein *self,
+ gdouble target_offset)
+{
+ GtkWidget *child;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+ g_assert (0.0 <= target_offset && target_offset <= 1.0);
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child == NULL || self->overlay_child == NULL)
+ return FALSE;
+
+ animate_stop (self);
+
+ if (!self->duration_set)
+ self->duration = gstyle_animation_check_enable_animation () ? compute_duration (self) : 0;
+
+ self->src_offset = self->offset;
+ self->dst_offset = target_offset;
+ gtk_widget_set_child_visible (child, TRUE);
+
+ if (self->src_offset == self->dst_offset)
+ return FALSE;
+
+ if (self->src_offset < self->dst_offset)
+ {
+ self->is_opening = TRUE;
+ g_signal_emit (self, signals [OPENING], 0);
+ }
+ else
+ {
+ self->is_closing = TRUE;
+ g_signal_emit (self, signals [CLOSING], 0);
+ }
+
+ if (self->duration == 0)
+ {
+ self->offset = target_offset;
+ animation_done_cb (self);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ }
+ else if (!self->animation_handler_id)
+ {
+ self->animation_starttime = g_get_monotonic_time();
+ self->animation_handler_id = gtk_widget_add_tick_callback (GTK_WIDGET (self),
+ animation_tick_cb,
+ self,
+ NULL);
+ }
+
+ return TRUE;
+}
+
+/**
+ * gstyle_slidein_get_animation_state:
+ * @direction: (out): slide animation state
+ *
+ * Get the animation state of the slide :
+ * %TRUE if the slide is currently animated, %FALSE otherwise.
+ *direction contains %TRUE if opening, %FALSE if closing.
+ *
+ * Returns: %TRUE if animating, %FALSE otherwise.
+ *
+ */
+gboolean
+gstyle_slidein_get_animation_state (GstyleSlidein *self,
+ gboolean *direction)
+{
+ gboolean is_animate;
+
+ g_return_val_if_fail (GSTYLE_IS_SLIDEIN (self), FALSE);
+
+ is_animate = (self->is_opening || self->is_closing);
+ if (is_animate)
+ *direction = self->is_opening;
+ else
+ *direction = self->revealed;
+
+ return is_animate;
+}
+
+/**
+ * gstyle_slidein_reveal_slide:
+ * @reveal: %TRUE to reveal or %FALSE to close the slide
+ *
+ * Reveal or close the slide.
+ *
+ * Returns: #TRUE if the action can be executed, otherwise, %FALSE if the slide is already
+ * in its final position, that is revealed or closed.
+ */
+gboolean
+gstyle_slidein_reveal_slide (GstyleSlidein *self,
+ gboolean reveal)
+{
+ GtkStyleContext *context;
+ GtkStateFlags state;
+
+ g_return_val_if_fail (GSTYLE_IS_SLIDEIN (self), FALSE);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ state = gtk_style_context_get_state (context);
+
+ if (get_orientation (self) == GTK_ORIENTATION_HORIZONTAL)
+ self->real_direction = (!!(state & GTK_STATE_FLAG_DIR_LTR)) ? self->direction_type
+ : self->direction_type_reverse;
+ else
+ self->real_direction = self->direction_type;
+
+ return animate (self, reveal ? 1.0 : 0.0);
+}
+
+/**
+ * gstyle_slidein_get_revealed:
+ *
+ * Get the state of the slide, revealed or not.
+ *
+ * Returns: %TRUE if the slide is already revealed, %FALSE otherwise.
+ */
+gboolean
+gstyle_slidein_get_revealed (GstyleSlidein *self)
+{
+ g_return_val_if_fail (GSTYLE_IS_SLIDEIN (self), FALSE);
+
+ return self->revealed;
+}
+
+static gboolean
+gstyle_slidein_event_box_key_pressed_cb (GstyleSlidein *self,
+ GdkEventKey *event,
+ GtkWidget *widget)
+{
+ GtkWidget *focus;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+ g_assert (event != NULL);
+ g_assert (GTK_IS_WIDGET (widget));
+
+ focus = gtk_window_get_focus (GTK_WINDOW (gtk_widget_get_toplevel (widget)));
+ if (focus == NULL)
+ return GDK_EVENT_PROPAGATE;
+
+ if (event->keyval == GDK_KEY_Escape && !GTK_IS_ENTRY (focus))
+ {
+ gstyle_slidein_reveal_slide (self, FALSE);
+ return GDK_EVENT_STOP;
+ }
+
+ if (gtk_widget_is_ancestor (focus, widget))
+ return gtk_widget_event (focus, (GdkEvent*) event);
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static void
+gstyle_slidein_compute_child_allocation (GstyleSlidein *self,
+ GtkAllocation parent_alloc,
+ GtkAllocation *child_alloc)
+{
+ GtkRequisition min_child_req, nat_child_req;
+ gint slide_max_visible_size;
+ gint margin;
+ gint offset_x = 0;
+ gint offset_y = 0;
+
+ child_alloc->width = parent_alloc.width;
+ child_alloc->height = parent_alloc.height;
+
+ gtk_widget_get_preferred_size (self->overlay_child, &min_child_req, &nat_child_req);
+
+ /* TODO: handle padding / margin */
+
+ if (get_orientation (self) == GTK_ORIENTATION_HORIZONTAL)
+ {
+ margin = MIN (self->slide_margin, parent_alloc.width);
+ slide_max_visible_size = parent_alloc.width - margin;
+ child_alloc->width = MAX (MAX (slide_max_visible_size * self->slide_fraction, 1), min_child_req.width);
+
+ if (self->real_direction == GSTYLE_SLIDEIN_DIRECTION_TYPE_LEFT)
+ offset_x = parent_alloc.width - (child_alloc->width * self->offset) + 0.5;
+ else
+ offset_x = (self->offset - 1) * child_alloc->width + 0.5;
+ }
+ else
+ {
+ margin = MIN (self->slide_margin, parent_alloc.height);
+ slide_max_visible_size = parent_alloc.height - margin;
+ child_alloc->height = MAX (MAX (slide_max_visible_size * self->slide_fraction, 1),
min_child_req.height);
+
+ if (self->direction_type == GSTYLE_SLIDEIN_DIRECTION_TYPE_UP)
+ offset_y = parent_alloc.height - (child_alloc->height * self->offset) + 0.5;
+ else
+ offset_y = (self->offset - 1) * child_alloc->height + 0.5;
+ }
+
+ child_alloc->x = parent_alloc.x + offset_x;
+ child_alloc->y = parent_alloc.y + offset_y;
+}
+
+static GdkWindow *
+gstyle_slidein_create_child_window (GstyleSlidein *self)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+ GtkAllocation parent_alloc;
+ GtkAllocation child_alloc;
+ GdkWindow *window;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &parent_alloc);
+ gstyle_slidein_compute_child_allocation (self, parent_alloc, &child_alloc);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.width = child_alloc.width;
+ attributes.height = child_alloc.height;
+ attributes.x = child_alloc.x;
+ attributes.y = child_alloc.y;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+ attributes.event_mask = gtk_widget_get_events (widget);
+
+ window = gdk_window_new (gtk_widget_get_window (widget), &attributes, attributes_mask);
+ gtk_widget_register_window (widget, window);
+ gtk_widget_set_parent_window (self->overlay_child, window);
+
+ return window;
+}
+
+static gboolean
+event_window_button_press_event_cb (GstyleSlidein *self,
+ GdkEvent *event,
+ GstyleSlidein *unused)
+{
+ GdkEventButton *button_event = (GdkEventButton *)event;
+ GtkAllocation alloc;
+ GtkAllocation child_alloc;
+ gboolean is_in_slide;
+ GtkWidget *src_widget;
+ gint dest_x, dest_y;
+ gint x1, y1;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ src_widget = gtk_get_event_widget (event);
+ gtk_widget_translate_coordinates (src_widget, GTK_WIDGET (self->overlay_child),
+ button_event->x, button_event->y,
+ &dest_x, &dest_y);
+
+ gtk_widget_get_allocated_size (self->overlay_child, &child_alloc, NULL);
+ x1 = alloc.x + child_alloc.width;
+ y1 = alloc.y + child_alloc.height;
+
+ is_in_slide = (alloc.x <= dest_x && dest_x <= x1 && alloc.y <= dest_y && dest_y <= y1);
+ if (!is_in_slide)
+ {
+ gtk_grab_remove (GTK_WIDGET (self));
+ gstyle_slidein_reveal_slide (self, FALSE);
+
+ return GDK_EVENT_PROPAGATE;
+ }
+ else
+ return GDK_EVENT_STOP;
+}
+
+static void
+gstyle_slidein_remove (GtkContainer *container,
+ GtkWidget *widget)
+{
+ GstyleSlidein *self = (GstyleSlidein *)container;
+ gboolean was_visible;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ if (widget == self->overlay_child)
+ {
+ if (self->overlay_window != NULL)
+ {
+ was_visible = gtk_widget_get_visible (widget);
+ gtk_widget_unregister_window (GTK_WIDGET (container), self->overlay_window);
+ gdk_window_destroy (self->overlay_window);
+ }
+
+ gtk_widget_unparent (widget);
+ self->overlay_child = NULL;
+ self->overlay_window = NULL;
+
+ if (was_visible)
+ gtk_widget_queue_resize(GTK_WIDGET(self));
+ }
+ else
+ GTK_CONTAINER_CLASS (gstyle_slidein_parent_class)->remove (container, widget);
+}
+
+/**
+ * gstyle_slidein_remove_slide:
+ *
+ * Remove, if any, the slide #GtkWidget.
+ *
+ */
+void
+gstyle_slidein_remove_slide (GstyleSlidein *self)
+{
+ g_return_if_fail (GSTYLE_IS_SLIDEIN (self));
+
+ if (self->overlay_child != NULL)
+ gstyle_slidein_remove (GTK_CONTAINER (self), self->overlay_child);
+}
+
+/**
+ * gstyle_slidein_add_slide:
+ * @slide: a #GtkWidget
+ *
+ * Set the #GtkWidget to use as a slide.
+ *
+ */
+void
+gstyle_slidein_add_slide (GstyleSlidein *self,
+ GtkWidget *slide)
+{
+ g_return_if_fail (GSTYLE_IS_SLIDEIN (self));
+ g_return_if_fail (GTK_IS_WIDGET (slide));
+
+ gstyle_slidein_remove_slide (self);
+
+ /* TODO: cchoose only one naming: overlay or slide */
+ self->overlay_child = slide;
+ if (gtk_widget_get_realized (GTK_WIDGET (self)))
+ self->overlay_window = gstyle_slidein_create_child_window (self);
+
+ gtk_widget_set_parent (slide, GTK_WIDGET (self));
+ if(gtk_widget_get_visible(slide))
+ gtk_widget_queue_resize(GTK_WIDGET(self));
+}
+
+static void
+gstyle_slidein_add_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *type)
+{
+ GstyleSlidein *self = (GstyleSlidein *)buildable;
+
+ g_assert (GSTYLE_SLIDEIN (self));
+ g_assert (GTK_IS_BUILDER (builder));
+ g_assert (G_IS_OBJECT (child));
+
+ if (!GTK_IS_WIDGET (child))
+ {
+ g_warning ("Attempt to add a child of type \"%s\" to a \"%s\"",
+ G_OBJECT_TYPE_NAME (child), G_OBJECT_TYPE_NAME (self));
+ return;
+ }
+
+ if (type != NULL && g_strcmp0 (type, "slide") == 0)
+ gstyle_slidein_add_slide (GSTYLE_SLIDEIN (buildable), GTK_WIDGET (child));
+ else if (type == NULL)
+ GTK_CONTAINER_CLASS (gstyle_slidein_parent_class)->add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
+ else
+ GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
+}
+
+static void
+gstyle_slidein_overlay_child_allocate (GstyleSlidein *self,
+ GtkAllocation *alloc)
+{
+ GtkAllocation child_alloc = { 0, };
+ gboolean visible;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+ g_assert (alloc != NULL);
+
+ if (self->overlay_child != NULL)
+ {
+ visible = gtk_widget_get_visible (self->overlay_child);
+ if (self->overlay_window != NULL && gtk_widget_get_mapped (GTK_WIDGET (self)))
+ {
+ if (visible)
+ gdk_window_show (self->overlay_window);
+ else if (gdk_window_is_visible (self->overlay_window))
+ gdk_window_hide (self->overlay_window);
+ }
+
+ if (!visible)
+ return;
+
+ gstyle_slidein_compute_child_allocation (self, *alloc, &child_alloc);
+
+ if (self->overlay_window != NULL)
+ gdk_window_move_resize (self->overlay_window,
+ child_alloc.x, child_alloc.y,
+ child_alloc.width, child_alloc.height);
+
+ child_alloc.x = 0;
+ child_alloc.y = 0;
+ gtk_widget_size_allocate (self->overlay_child, &child_alloc);
+ }
+}
+
+static void
+gstyle_slidein_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GstyleSlidein *self = (GstyleSlidein *)widget;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+ g_assert (allocation != NULL);
+
+ GTK_WIDGET_CLASS (gstyle_slidein_parent_class)->size_allocate (widget, allocation);
+
+ gstyle_slidein_overlay_child_allocate (self, allocation);
+}
+
+static void
+gstyle_slidein_get_preferred_width (GtkWidget *widget,
+ gint *min_width,
+ gint *nat_width)
+{
+ GstyleSlidein *self = (GstyleSlidein *)widget;
+ GtkWidget *child;
+ gint min_width_slide_based;
+ gint nat_width_slide_based;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ *min_width = *nat_width = 1;
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child != NULL)
+ gtk_widget_get_preferred_width (child, min_width, nat_width);
+
+ if (self->interpolate_size ||
+ (self->overlay_child != NULL && gtk_widget_get_visible (self->overlay_child)))
+ {
+ gtk_widget_get_preferred_width (self->overlay_child, &min_width_slide_based, &nat_width_slide_based);
+ if (get_orientation (self) == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (!self->interpolate_size)
+ {
+ min_width_slide_based *= self->offset;
+ nat_width_slide_based *= self->offset;
+ }
+
+ if (self->slide_fraction > 0)
+ {
+ min_width_slide_based /= self->slide_fraction;
+ nat_width_slide_based /= self->slide_fraction;
+ }
+
+ min_width_slide_based += self->slide_margin;
+ nat_width_slide_based += self->slide_margin;
+ }
+
+ /* TODO: add dynamic grow/shrink mode */
+ *min_width = MAX (*min_width, min_width_slide_based);
+ *nat_width = MAX (*nat_width, nat_width_slide_based);
+ }
+ else
+ {
+ *min_width = MAX (*min_width, self->slide_margin);
+ *nat_width = MAX (*nat_width, self->slide_margin);
+ }
+}
+
+static void
+gstyle_slidein_get_preferred_height (GtkWidget *widget,
+ gint *min_height,
+ gint *nat_height)
+{
+ GstyleSlidein *self = (GstyleSlidein *)widget;
+ GtkWidget *child;
+ gint min_height_slide_based;
+ gint nat_height_slide_based;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ *min_height = *nat_height = 1;
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child != NULL)
+ gtk_widget_get_preferred_width (child, min_height, nat_height);
+
+ if (self->interpolate_size ||
+ (self->overlay_child != NULL && gtk_widget_get_visible (self->overlay_child)))
+ {
+ gtk_widget_get_preferred_height (self->overlay_child, &min_height_slide_based,
&nat_height_slide_based);
+ if (get_orientation (self) == GTK_ORIENTATION_VERTICAL)
+ {
+ if (!self->interpolate_size)
+ {
+ min_height_slide_based *= self->offset;
+ nat_height_slide_based *= self->offset;
+ }
+
+ if (self->slide_fraction > 0)
+ {
+ min_height_slide_based /= self->slide_fraction;
+ nat_height_slide_based /= self->slide_fraction;
+ }
+
+ min_height_slide_based += self->slide_margin;
+ nat_height_slide_based += self->slide_margin;
+ }
+
+ /* TODO: add dynamic grow/shrink mode */
+ *min_height = MAX (*min_height, min_height_slide_based);
+ *nat_height = MAX (*nat_height, nat_height_slide_based);
+ }
+ else
+ {
+ *min_height = MAX (*min_height, self->slide_margin);
+ *nat_height = MAX (*nat_height, self->slide_margin);
+ }
+}
+
+static void
+gstyle_slidein_realize (GtkWidget *widget)
+{
+ GstyleSlidein *self = (GstyleSlidein *)widget;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ GTK_WIDGET_CLASS (gstyle_slidein_parent_class)->realize (widget);
+ gtk_widget_set_realized (widget, TRUE);
+
+ if (self->overlay_window == NULL)
+ self->overlay_window = gstyle_slidein_create_child_window (self);
+}
+
+static void
+gstyle_slidein_unrealize (GtkWidget *widget)
+{
+ GstyleSlidein *self = (GstyleSlidein *)widget;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ if (self->overlay_child != NULL && self->overlay_window != NULL)
+ {
+ gtk_widget_set_parent_window (self->overlay_child, NULL);
+ gtk_widget_unregister_window (widget, self->overlay_window);
+ gdk_window_destroy (self->overlay_window);
+ self->overlay_window = NULL;
+ }
+
+ GTK_WIDGET_CLASS (gstyle_slidein_parent_class)->unrealize (widget);
+}
+
+static void
+gstyle_slidein_map (GtkWidget *widget)
+{
+ GstyleSlidein *self = (GstyleSlidein *)widget;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ GTK_WIDGET_CLASS (gstyle_slidein_parent_class)->map (widget);
+
+ if (self->overlay_child != NULL &&
+ self->overlay_window != NULL &&
+ gtk_widget_get_visible (self->overlay_child) &&
+ gtk_widget_get_child_visible (self->overlay_child))
+ {
+ gdk_window_show (self->overlay_window);
+ g_signal_connect_swapped (self, "button-press-event",
+ G_CALLBACK(event_window_button_press_event_cb),
+ self);
+ }
+}
+
+static void
+gstyle_slidein_unmap (GtkWidget *widget)
+{
+ GstyleSlidein *self = (GstyleSlidein *)widget;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ if (self->overlay_child != NULL &&
+ self->overlay_window != NULL &&
+ gtk_widget_is_visible (self->overlay_child))
+ {
+ gdk_window_hide (self->overlay_window );
+ g_signal_handlers_disconnect_by_func (self->overlay_child,
+ event_window_button_press_event_cb,
+ self);
+ }
+
+ GTK_WIDGET_CLASS (gstyle_slidein_parent_class)->unmap (widget);
+}
+
+static gboolean
+gstyle_slidein_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ GstyleSlidein *self = (GstyleSlidein *)widget;
+ GtkStyleContext *context;
+ GtkAllocation shade_box;
+ GtkWidget *child;
+ GdkRGBA rgba;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+ g_assert (cr != NULL);
+
+ /* To draw the shade effect in between the regular child and the slides,
+ * we bypass gtk_event_box_draw (we use a windowless one so not a problem),
+ * and provide your own container draw implementation.
+ */
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child == NULL)
+ return GDK_EVENT_STOP;
+
+ gtk_container_propagate_draw (GTK_CONTAINER (self), child, cr);
+
+ if (self->offset > 0.0)
+ {
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_save (context);
+ gtk_style_context_add_class (context, "shade");
+ gtk_style_context_get_color (context, gtk_style_context_get_state (context), &rgba);
+ gtk_style_context_restore (context);
+ rgba.alpha = rgba.alpha * self->offset;
+
+ /* We shade the whole surface in case of slide tranparency */
+ gtk_widget_get_allocated_size (widget, &shade_box, NULL);
+ cairo_rectangle (cr, shade_box.x, shade_box.y, shade_box.width, shade_box.height);
+ gdk_cairo_set_source_rgba (cr, &rgba);
+ cairo_fill (cr);
+ }
+
+ if (self->overlay_child != NULL)
+ gtk_container_propagate_draw (GTK_CONTAINER (self), self->overlay_child, cr);
+
+ return GDK_EVENT_STOP;
+}
+
+static void
+gstyle_slidein_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ GstyleSlidein *self = (GstyleSlidein *)container;
+ GtkWidget *child;
+
+ g_assert (GSTYLE_IS_SLIDEIN (self));
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child)
+ (* callback) (child, callback_data);
+
+ if (self->overlay_child)
+ (* callback) (self->overlay_child, callback_data);
+}
+
+GtkWidget *
+gstyle_slidein_new (void)
+{
+ return g_object_new (GSTYLE_TYPE_SLIDEIN, NULL);
+}
+
+static void
+gstyle_slidein_finalize (GObject *object)
+{
+ GstyleSlidein *self = (GstyleSlidein *)object;
+
+ g_clear_object (&self->default_provider);
+ g_clear_object (&self->overlay_child);
+
+ G_OBJECT_CLASS (gstyle_slidein_parent_class)->finalize (object);
+}
+
+static void
+gstyle_slidein_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleSlidein *self = GSTYLE_SLIDEIN (object);
+
+ switch (prop_id)
+ {
+ case PROP_DIRECTION_TYPE:
+ g_value_set_enum (value, gstyle_slidein_get_direction_type (self));
+ break;
+
+ case PROP_DURATION:
+ g_value_set_double (value, gstyle_slidein_get_duration (self));
+ break;
+
+ case PROP_INTERPOLATE_SIZE:
+ g_value_set_boolean (value, gstyle_slidein_get_interpolate_size (self));
+ break;
+
+ case PROP_REVEALED:
+ g_value_set_boolean (value, gstyle_slidein_get_revealed (self));
+ break;
+
+ case PROP_SLIDE_FRACTION:
+ g_value_set_double (value, gstyle_slidein_get_slide_fraction (self));
+ break;
+
+ case PROP_SLIDE_MARGIN:
+ g_value_set_uint (value, gstyle_slidein_get_slide_margin (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_slidein_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstyleSlidein *self = GSTYLE_SLIDEIN (object);
+
+ switch (prop_id)
+ {
+ case PROP_DIRECTION_TYPE:
+ gstyle_slidein_set_direction_type (self, g_value_get_enum (value));
+ break;
+
+ case PROP_DURATION:
+ gstyle_slidein_set_duration (self, g_value_get_double (value));
+ break;
+
+ case PROP_INTERPOLATE_SIZE:
+ gstyle_slidein_set_interpolate_size (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_SLIDE_FRACTION:
+ gstyle_slidein_set_slide_fraction (self, g_value_get_double (value));
+ break;
+
+ case PROP_SLIDE_MARGIN:
+ gstyle_slidein_set_slide_margin (self, g_value_get_uint (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gstyle_slidein_class_init (GstyleSlideinClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+ object_class->finalize = gstyle_slidein_finalize;
+ object_class->get_property = gstyle_slidein_get_property;
+ object_class->set_property = gstyle_slidein_set_property;
+
+ widget_class->size_allocate = gstyle_slidein_size_allocate;
+ widget_class->get_preferred_width = gstyle_slidein_get_preferred_width;
+ widget_class->get_preferred_height = gstyle_slidein_get_preferred_height;
+ widget_class->realize = gstyle_slidein_realize;
+ widget_class->unrealize = gstyle_slidein_unrealize;
+ widget_class->map = gstyle_slidein_map;
+ widget_class->unmap = gstyle_slidein_unmap;
+ widget_class->draw = gstyle_slidein_draw;
+
+ container_class->remove = gstyle_slidein_remove;
+ container_class->forall = gstyle_slidein_forall;
+
+ properties [PROP_DURATION] =
+ g_param_spec_double ("duration",
+ "duration",
+ "slide animation time in ms",
+ 0.0,
+ G_MAXDOUBLE,
+ 0.0,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_INTERPOLATE_SIZE] =
+ g_param_spec_boolean ("interpolate-size",
+ "interpolate-size",
+ "interpolate-size",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SLIDE_FRACTION] =
+ g_param_spec_double ("slide-fraction",
+ "slide-fraction",
+ "fraction to show when releaving the slide",
+ 0.0,
+ 1.0,
+ 1.0,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SLIDE_MARGIN] =
+ g_param_spec_uint ("slide-margin",
+ "slide-margin",
+ "margin to keep when releaving the slide",
+ 0,
+ G_MAXUINT,
+ 0,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_REVEALED] =
+ g_param_spec_boolean ("revealed",
+ "revealed",
+ "Whether the slidein is revealed",
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DIRECTION_TYPE] =
+ g_param_spec_enum ("direction-type",
+ "direction-type",
+ "direction-type",
+ GSTYLE_TYPE_SLIDEIN_DIRECTION_TYPE,
+ GSTYLE_SLIDEIN_DIRECTION_TYPE_LEFT,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ /**
+ * GstyleSlidein::revealed:
+ * @self: #GstyleSlidein instance.
+ * @direction_type: type of opening direction.
+ * @revealed: the revealed state of the slide.
+ *
+ * Emitted when the revealed state of the slide child change.
+ * Notice that this only append after the duration time
+ */
+ signals[REVEALED] = g_signal_new ("revealed",
+ GSTYLE_TYPE_SLIDEIN,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 2,
+ GSTYLE_TYPE_SLIDEIN_DIRECTION_TYPE,
+ G_TYPE_BOOLEAN);
+
+ /**
+ * GstyleSlidein::closing:
+ * @self: #GstyleSlidein instance.
+ *
+ * Emitted when the slide start closing.
+ */
+ signals[CLOSING] = g_signal_new ("closing",
+ GSTYLE_TYPE_SLIDEIN,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * GstyleSlidein::opening:
+ * @self: #GstyleSlidein instance.
+ *
+ * Emitted when the slide start opening.
+ */
+ signals[OPENING] = g_signal_new ("opening",
+ GSTYLE_TYPE_SLIDEIN,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ gtk_widget_class_set_css_name (widget_class, "gstyleslidein");
+}
+
+static void
+gstyle_slidein_init (GstyleSlidein *self)
+{
+ GtkStyleContext *context;
+
+ g_signal_connect_swapped (self,
+ "key-press-event",
+ G_CALLBACK (gstyle_slidein_event_box_key_pressed_cb),
+ self);
+
+ gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX (self), FALSE);
+ gtk_event_box_set_above_child (GTK_EVENT_BOX (self), FALSE);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ self->default_provider = gstyle_css_provider_init_default (gtk_style_context_get_screen (context));
+
+ self->direction_type = GSTYLE_SLIDEIN_DIRECTION_TYPE_RIGHT;
+ self->direction_type_reverse = GSTYLE_SLIDEIN_DIRECTION_TYPE_LEFT;
+ self->duration = 0.0;
+ self->duration_set = TRUE;
+}
+
+static void
+gstyle_slidein_init_buildable_iface (GtkBuildableIface *iface)
+{
+ iface->add_child = gstyle_slidein_add_child;
+}
+
+GType
+gstyle_slidein_type_get_type (void)
+{
+ static GType type_id;
+ static const GEnumValue values[] = {
+ { GSTYLE_SLIDEIN_DIRECTION_TYPE_NONE, "GSTYLE_SLIDEIN_DIRECTION_TYPE_NONE", "none" },
+ { GSTYLE_SLIDEIN_DIRECTION_TYPE_RIGHT, "GSTYLE_SLIDEIN_DIRECTION_TYPE_RIGHT", "right" },
+ { GSTYLE_SLIDEIN_DIRECTION_TYPE_LEFT, "GSTYLE_SLIDEIN_DIRECTION_TYPE_LEFT", "left" },
+ { GSTYLE_SLIDEIN_DIRECTION_TYPE_UP, "GSTYLE_SLIDEIN_DIRECTION_TYPE_UP", "up" },
+ { GSTYLE_SLIDEIN_DIRECTION_TYPE_DOWN, "GSTYLE_SLIDEIN_DIRECTION_TYPE_DOWN", "down" },
+ { 0 }
+ };
+
+ if (g_once_init_enter (&type_id))
+ {
+ GType _type_id;
+
+ _type_id = g_enum_register_static ("GstyleSlideinDirectionType", values);
+ g_once_init_leave (&type_id, _type_id);
+ }
+
+ return type_id;
+}
diff --git a/contrib/gstyle/gstyle-slidein.h b/contrib/gstyle/gstyle-slidein.h
new file mode 100644
index 0000000..1691193
--- /dev/null
+++ b/contrib/gstyle/gstyle-slidein.h
@@ -0,0 +1,64 @@
+/* gstyle-slidein.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_SLIDEIN_H
+#define GSTYLE_SLIDEIN_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_SLIDEIN_DIRECTION_TYPE (gstyle_slidein_type_get_type())
+
+typedef enum
+{
+ GSTYLE_SLIDEIN_DIRECTION_TYPE_NONE,
+ GSTYLE_SLIDEIN_DIRECTION_TYPE_RIGHT,
+ GSTYLE_SLIDEIN_DIRECTION_TYPE_LEFT,
+ GSTYLE_SLIDEIN_DIRECTION_TYPE_UP,
+ GSTYLE_SLIDEIN_DIRECTION_TYPE_DOWN,
+} GstyleSlideinDirectionType;
+
+#define GSTYLE_TYPE_SLIDEIN (gstyle_slidein_get_type())
+
+G_DECLARE_FINAL_TYPE (GstyleSlidein, gstyle_slidein, GSTYLE, SLIDEIN, GtkEventBox)
+
+GType gstyle_slidein_type_get_type (void);
+
+GtkWidget *gstyle_slidein_new (void);
+void gstyle_slidein_add_slide (GstyleSlidein
*self,
+ GtkWidget
*slide);
+void gstyle_slidein_remove_slide (GstyleSlidein
*self);
+gboolean gstyle_slidein_reveal_slide (GstyleSlidein
*self,
+ gboolean
reveal);
+
+GstyleSlideinDirectionType gstyle_slidein_get_direction_type (GstyleSlidein
*self);
+gboolean gstyle_slidein_get_interpolate_size (GstyleSlidein
*self);
+gboolean gstyle_slidein_get_revealed (GstyleSlidein
*self);
+gdouble gstyle_slidein_get_slide_fraction (GstyleSlidein
*self);
+
+void gstyle_slidein_set_direction_type (GstyleSlidein
*self,
+ GstyleSlideinDirectionType
direction_type);
+void gstyle_slidein_set_interpolate_size (GstyleSlidein
*self,
+ gboolean
interpolate_size);
+void gstyle_slidein_set_slide_fraction (GstyleSlidein
*self,
+ gdouble
slide_fraction);
+G_END_DECLS
+
+#endif /* GSTYLE_SLIDEIN_H */
diff --git a/contrib/gstyle/gstyle-types.h b/contrib/gstyle/gstyle-types.h
new file mode 100644
index 0000000..30b9534
--- /dev/null
+++ b/contrib/gstyle/gstyle-types.h
@@ -0,0 +1,34 @@
+/* gstyle-types.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_TYPES_H
+#define GSTYLE_TYPES_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstyleCielab GstyleCielab;
+typedef struct _GstyleHSV GstyleHSV;
+typedef struct _GstyleXYZ GstyleXYZ;
+typedef struct _GstyleColorItem GstyleColorItem;
+
+G_END_DECLS
+
+#endif /* GSTYLE_TYPES_H */
+
diff --git a/contrib/gstyle/gstyle-utils.c b/contrib/gstyle/gstyle-utils.c
new file mode 100644
index 0000000..7ed4b73
--- /dev/null
+++ b/contrib/gstyle/gstyle-utils.c
@@ -0,0 +1,172 @@
+/* gstyle-utils.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gstyle-utils.h"
+
+gboolean
+gstyle_str_empty0 (const gchar *str)
+{
+ return (str == NULL) || (str[0] == '\0');
+}
+
+gboolean
+gstyle_utf8_is_spaces (const gchar *str)
+{
+ gunichar c;
+
+ if (str == NULL)
+ return FALSE;
+
+ while (g_unichar_isspace (c = g_utf8_get_char (str)))
+ str = g_utf8_next_char (str);
+
+ return (c == '\0');
+}
+
+typedef enum
+{
+ CORNER_TOP_LEFT,
+ CORNER_TOP_RIGHT,
+ CORNER_BOTTOM_LEFT,
+ CORNER_BOTTOM_RIGHT
+} Corner;
+
+static void
+draw_corner (cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble radius,
+ Corner corner)
+{
+ switch (corner)
+ {
+ case CORNER_TOP_LEFT:
+ cairo_arc (cr, x + radius, y + radius, radius, -G_PI, -G_PI_2);
+ break;
+
+ case CORNER_TOP_RIGHT:
+ cairo_arc (cr, x - radius, y + radius, radius, -G_PI_2, 0.0);
+ break;
+
+ case CORNER_BOTTOM_RIGHT:
+ cairo_arc (cr, x - radius, y - radius, radius, 0.0, G_PI_2);
+ break;
+
+ case CORNER_BOTTOM_LEFT:
+ cairo_arc (cr, x + radius, y - radius, radius, G_PI_2, G_PI);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+void
+draw_cairo_round_box (cairo_t *cr,
+ GdkRectangle rect,
+ gint tl_radius,
+ gint tr_radius,
+ gint bl_radius,
+ gint br_radius)
+{
+ gdouble right = rect.x + rect.width;
+ gdouble bottom = rect.y + rect.height;
+
+ cairo_new_sub_path (cr);
+ cairo_move_to (cr, rect.x, rect.y + tl_radius);
+
+ if (tl_radius > 0)
+ draw_corner (cr, rect.x, rect.y, tl_radius, CORNER_TOP_LEFT);
+
+ cairo_line_to (cr, right - tr_radius, rect.y);
+
+ if (tr_radius > 0)
+ draw_corner (cr, right, rect.y, tr_radius, CORNER_TOP_RIGHT);
+
+ cairo_line_to (cr, right, bottom - br_radius);
+
+ if (br_radius > 0)
+ draw_corner (cr, right, bottom, br_radius, CORNER_BOTTOM_RIGHT);
+
+ cairo_line_to (cr, rect.x + bl_radius, bottom);
+
+ if (br_radius > 0)
+ draw_corner (cr, rect.x, bottom, bl_radius, CORNER_BOTTOM_LEFT);
+
+ cairo_close_path (cr);
+}
+
+void
+gstyle_utils_get_rect_resized_box (GdkRectangle src_rect,
+ GdkRectangle *dst_rect,
+ GtkBorder *offset)
+{
+ dst_rect->x = src_rect.x + offset->left;
+ dst_rect->y = src_rect.y + offset->top;
+ dst_rect->width = src_rect.width - (offset->left + offset->right);
+ dst_rect->height = src_rect.height - (offset->top + offset->bottom);
+
+ if (dst_rect->width < 1)
+ {
+ dst_rect->width = 1;
+ dst_rect->x = src_rect.x + src_rect.width / 2;
+ }
+
+ if (dst_rect->height < 1)
+ {
+ dst_rect->height = 1;
+ dst_rect->y = src_rect.y + src_rect.height / 2;
+ }
+}
+
+cairo_pattern_t *
+gstyle_utils_get_checkered_pattern (void)
+{
+ static unsigned char data[8] = { 0xFF, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0x00, 0x00 };
+ cairo_surface_t *surface;
+ cairo_pattern_t *pattern;
+
+ surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_A8, 2, 2, 4);
+ pattern = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+ cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST);
+ cairo_surface_destroy (surface);
+
+ return pattern;
+}
+
+void
+gstyle_utils_get_contrasted_rgba (GdkRGBA rgba,
+ GdkRGBA *dst_rgba)
+{
+ guint brightness;
+
+ brightness = rgba.red * 299 + rgba.green * 587 + rgba.blue * 114;
+ if (brightness > 500)
+ {
+ dst_rgba->red = dst_rgba->green = dst_rgba->blue = 0.0;
+ }
+ else
+ {
+ dst_rgba->red = dst_rgba->green = dst_rgba->blue = 1.0;
+ }
+
+ rgba.alpha = 1.0;
+}
diff --git a/contrib/gstyle/gstyle-utils.h b/contrib/gstyle/gstyle-utils.h
new file mode 100644
index 0000000..8bae505
--- /dev/null
+++ b/contrib/gstyle/gstyle-utils.h
@@ -0,0 +1,93 @@
+/* gstyle-utils.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_UTILS_H
+#define GSTYLE_UTILS_H
+
+#include <glib.h>
+#include <cairo.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+gboolean gstyle_str_empty0 (const gchar *str);
+gboolean gstyle_utf8_is_spaces (const gchar *str);
+void draw_cairo_round_box (cairo_t *cr,
+ GdkRectangle rect,
+ gint tl_radius,
+ gint tr_radius,
+ gint bl_radius,
+ gint br_radius);
+void gstyle_utils_get_rect_resized_box (GdkRectangle src_rect,
+ GdkRectangle *dst_rect,
+ GtkBorder *offset);
+cairo_pattern_t *gstyle_utils_get_checkered_pattern (void);
+void gstyle_utils_get_contrasted_rgba (GdkRGBA rgba,
+ GdkRGBA *dst_rgba);
+
+static inline guint32
+pack_rgba24 (GdkRGBA *rgba)
+{
+ guint red, green, blue, alpha;
+ guint32 result;
+
+ alpha = CLAMP (rgba->alpha * 255, 0, 255);
+ red = CLAMP (rgba->red * 255, 0, 255);
+ green = CLAMP (rgba->green * 255, 0, 255);
+ blue = CLAMP (rgba->blue * 255, 0, 255);
+ result = (alpha << 24) | (red << 16) | (green << 8) | blue;
+
+ return result;
+}
+
+static inline void
+unpack_rgba24 (guint32 val,
+ GdkRGBA *rgba)
+{
+ rgba->blue = (val & 0xFF) / 255.0;
+ val >>= 8;
+ rgba->green = (val & 0xFF) / 255.0;
+ val >>= 8;
+ rgba->red = (val & 0xFF) / 255.0;
+ val >>= 8;
+ rgba->alpha = (val & 0xFF) / 255.0;
+}
+
+static inline gboolean
+gstyle_utils_cmp_border (GtkBorder border1,
+ GtkBorder border2)
+{
+ if (border1.left != border2.left ||
+ border1.right != border2.right ||
+ border1.top != border2.top ||
+ border1.bottom != border2.bottom)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+#define gstyle_clear_weak_pointer(ptr) \
+ (*(ptr) ? (g_object_remove_weak_pointer((GObject*)*(ptr), (gpointer*)ptr),*(ptr)=NULL,1) : 0)
+
+#define gstyle_set_weak_pointer(ptr,obj) \
+
((obj!=*(ptr))?(gstyle_clear_weak_pointer(ptr),*(ptr)=obj,((obj)?g_object_add_weak_pointer((GObject*)obj,(gpointer*)ptr),NULL:NULL),1):0)
+
+G_END_DECLS
+
+#endif /* GSTYLE_UTILS_H */
diff --git a/contrib/gstyle/gstyle-xyz.c b/contrib/gstyle/gstyle-xyz.c
new file mode 100644
index 0000000..afe602c
--- /dev/null
+++ b/contrib/gstyle/gstyle-xyz.c
@@ -0,0 +1,64 @@
+/* gstyle-xyz.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "gstyle-xyz"
+
+#include "gstyle-xyz.h"
+
+G_DEFINE_BOXED_TYPE (GstyleXYZ, gstyle_xyz, gstyle_xyz_copy, gstyle_xyz_free)
+
+/**
+ * GstyleXYZ:
+ * @x: Tristimulus X value.
+ * @y: Tristimulus Y value.
+ * @z: Tristimulus Z value.
+ * @alpha: The opacity of the color in [0, 1] range.
+ *
+ * A #GstyleXYZ is used to represent a color in
+ * the CIE 1931 XYZ color space.
+ */
+
+/**
+ * gstyle_xyz_copy:
+ * @self: a #GstyleXYZ
+ *
+ * Makes a copy of a #GstyleXYZ.
+ *
+ * The result must be freed through gstyle_xyz_free().
+ *
+ * Returns: a newly allocated #GstyleXYZ with the same content as @self.
+ *
+ */
+GstyleXYZ *
+gstyle_xyz_copy (const GstyleXYZ *self)
+{
+ return g_slice_dup (GstyleXYZ, self);
+}
+
+/**
+ * gstyle_xyz_free:
+ * @self: a #GstyleXYZ
+ *
+ * Frees a #GstyleXYZ created with gstyle_xyz_copy().
+ *
+ */
+void
+gstyle_xyz_free (GstyleXYZ *self)
+{
+ g_slice_free (GstyleXYZ, self);
+}
diff --git a/contrib/gstyle/gstyle-xyz.h b/contrib/gstyle/gstyle-xyz.h
new file mode 100644
index 0000000..1b3550f
--- /dev/null
+++ b/contrib/gstyle/gstyle-xyz.h
@@ -0,0 +1,45 @@
+/* gstyle-xyz.h
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GSTYLE_XYZ_H
+#define GSTYLE_XYZ_H
+
+#include <glib.h>
+
+#include "gstyle-types.h"
+
+G_BEGIN_DECLS
+
+#define GSTYLE_TYPE_XYZ (gstyle_xyz_get_type ())
+
+struct _GstyleXYZ
+{
+ gdouble x;
+ gdouble y;
+ gdouble z;
+ gdouble alpha;
+};
+
+GType gstyle_xyz_get_type (void) G_GNUC_CONST;
+
+GstyleXYZ *gstyle_xyz_copy (const GstyleXYZ *self);
+void gstyle_xyz_free (GstyleXYZ *self);
+
+G_END_DECLS
+
+#endif /* GSTYLE_XYZ_H */
diff --git a/contrib/gstyle/gstyle.gresource.xml b/contrib/gstyle/gstyle.gresource.xml
new file mode 100644
index 0000000..4a9a003
--- /dev/null
+++ b/contrib/gstyle/gstyle.gresource.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/libgstyle">
+ <file compressed="true" alias="ui/gstyle-color-panel.ui">ui/gstyle-color-panel.ui</file>
+ <file compressed="true" alias="ui/gstyle-palette-widget.ui">ui/gstyle-palette-widget.ui</file>
+
+ <file compressed="true" alias="theme/gstyle.css">theme/gstyle.css</file>
+
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-horizontal.png">assets/color-scale-slider-horizontal.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-horizontal-hover.png">assets/color-scale-slider-horizontal-hover.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-horizontal-active.png">assets/color-scale-slider-horizontal-active.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-horizontal-insensitive.png">assets/color-scale-slider-horizontal-insensitive.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-horizontal-backdrop.png">assets/color-scale-slider-horizontal-backdrop.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-horizontal-backdrop-insensitive.png">assets/color-scale-slider-horizontal-backdrop-insensitive.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-vertical.png">assets/color-scale-slider-vertical.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-vertical-hover.png">assets/color-scale-slider-vertical-hover.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-vertical-active.png">assets/color-scale-slider-vertical-active.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-vertical-insensitive.png">assets/color-scale-slider-vertical-insensitive.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-vertical-backdrop.png">assets/color-scale-slider-vertical-backdrop.png</file>
+ <file preprocess='to-pixdata'
alias="colorscale/color-scale-slider-vertical-backdrop-insensitive.png">assets/color-scale-slider-vertical-backdrop-insensitive.png</file>
+
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-horizontal 2
png">assets/color-scale-slider-horizontal 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-horizontal-hover 2
png">assets/color-scale-slider-horizontal-hover 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-horizontal-active 2
png">assets/color-scale-slider-horizontal-active 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-horizontal-insensitive 2
png">assets/color-scale-slider-horizontal-insensitive 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-horizontal-backdrop 2
png">assets/color-scale-slider-horizontal-backdrop 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-horizontal-backdrop-insensitive 2
png">assets/color-scale-slider-horizontal-backdrop-insensitive 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-vertical 2
png">assets/color-scale-slider-vertical 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-vertical-hover 2
png">assets/color-scale-slider-vertical-hover 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-vertical-active 2
png">assets/color-scale-slider-vertical-active 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-vertical-insensitive 2
png">assets/color-scale-slider-vertical-insensitive 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-vertical-backdrop 2
png">assets/color-scale-slider-vertical-backdrop 2 png</file>
+ <file preprocess='to-pixdata' alias="colorscale/color-scale-slider-vertical-backdrop-insensitive 2
png">assets/color-scale-slider-vertical-backdrop-insensitive 2 png</file>
+
+ <file alias="icons/scalable/actions/eyedropper-symbolic.svg">assets/eyedropper-symbolic.svg</file>
+
+ <file alias="icons/unit-percent-symbolic.svg">assets/unit-percent-symbolic.svg</file>
+ <file alias="icons/unit-degree-symbolic.svg">assets/unit-degree-symbolic.svg</file>
+ </gresource>
+</gresources>
diff --git a/contrib/gstyle/tests/Makefile.am b/contrib/gstyle/tests/Makefile.am
new file mode 100644
index 0000000..1e830ff
--- /dev/null
+++ b/contrib/gstyle/tests/Makefile.am
@@ -0,0 +1,94 @@
+EXTRA_DIST =
+
+gstyle_cflags = \
+ $(DEBUG_CFLAGS) \
+ $(GSTYLE_CFLAGS) \
+ -I$(top_srcdir)/contrib/gstyle \
+ -DTEST_DATA_DIR="\"$(abs_builddir)/data\"" \
+ $(NULL)
+
+gstyle_libs = \
+ $(GSTYLE_LIBS) \
+ $(top_builddir)/contrib/gstyle/libgstyle-private.la \
+ $(NULL)
+
+tests_cflags = \
+ $(egg_cflags) \
+ $(gstyle_cflags) \
+ $(NULL)
+
+tests_libs = \
+ $(gstyle_libs) \
+ $(top_builddir)/contrib/egg/libegg-private.la \
+ $(NULL)
+
+tests_ldflags = \
+ -export-dynamic \
+ $(NULL)
+
+misc_programs =
+
+TESTS_ENVIRONMENT = \
+ GB_IN_TREE_PLUGINS=1 \
+ G_TEST_SRCDIR="$(abs_srcdir)" \
+ G_TEST_BUILDDIR="$(abs_builddir)" \
+ G_DEBUG=gc-friendly \
+ GSETTINGS_BACKEND=memory \
+ MALLOC_CHECK_=2 \
+ MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) \
+ $(NULL)
+
+LOG_COMPILER = $(top_srcdir)/build/autotools/tap-test
+
+misc_programs = test-gstyle-color
+test_gstyle_color_SOURCES = test-gstyle-color.c
+test_gstyle_color_CFLAGS = $(tests_cflags)
+test_gstyle_color_LDADD = $(tests_libs)
+
+misc_programs += test-gstyle-color-panel
+test_gstyle_color_panel_SOURCES = test-gstyle-color-panel.c
+test_gstyle_color_panel_CFLAGS = $(tests_cflags)
+test_gstyle_color_panel_LDADD = $(tests_libs)
+
+misc_programs += test-gstyle-color-plane
+test_gstyle_color_plane_SOURCES = test-gstyle-color-plane.c
+test_gstyle_color_plane_CFLAGS = $(tests_cflags)
+test_gstyle_color_plane_LDADD = $(tests_libs)
+
+misc_programs += test-gstyle-color-scale
+test_gstyle_color_scale_SOURCES = test-gstyle-color-scale.c
+test_gstyle_color_scale_CFLAGS = $(tests_cflags)
+test_gstyle_color_scale_LDADD = $(tests_libs)
+
+misc_programs += test-gstyle-color-widget
+test_gstyle_color_widget_SOURCES = test-gstyle-color-widget.c
+test_gstyle_color_widget_CFLAGS = $(tests_cflags)
+test_gstyle_color_widget_LDADD = $(tests_libs)
+
+misc_programs += test-gstyle-palette
+test_gstyle_palette_SOURCES = test-gstyle-palette.c
+test_gstyle_palette_CFLAGS = $(tests_cflags)
+test_gstyle_palette_LDADD = $(tests_libs)
+
+misc_programs += test-gstyle-palette-widget
+test_gstyle_palette_widget_SOURCES = test-gstyle-palette-widget.c
+test_gstyle_palette_widget_CFLAGS = $(tests_cflags)
+test_gstyle_palette_widget_LDADD = $(tests_libs)
+
+misc_programs += test-gstyle-parse
+test_gstyle_parse_SOURCES = test-gstyle-parse.c
+test_gstyle_parse_CFLAGS = $(tests_cflags)
+test_gstyle_parse_LDADD = $(tests_libs)
+
+if ENABLE_TESTS
+noinst_PROGRAMS = $(TESTS) $(misc_programs)
+endif
+
+check_PROGRAMS = $(TESTS) $(misc_programs)
+
+EXTRA_DIST += \
+ data/palette.gpl \
+ data/palette.xml \
+ $(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/contrib/gstyle/tests/data/gstyle-color-editor.ui
b/contrib/gstyle/tests/data/gstyle-color-editor.ui
new file mode 100644
index 0000000..e74c9fe
--- /dev/null
+++ b/contrib/gstyle/tests/data/gstyle-color-editor.ui
@@ -0,0 +1,280 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="editor_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorPlane" id="plane">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="expand">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="mode_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="model">mode_store</property>
+ <child>
+ <object class="GtkCellRendererText"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">6</property>
+ <property name="row_homogeneous">True</property>
+ <property name="column_homogeneous">True</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Hue</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Saturation</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Value</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Cielab l</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Cielab a</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Cielab b</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Red</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Green</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Blue</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="hsv_h_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="round_digits">1</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="hsv_s_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="round_digits">1</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="hsv_v_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="round_digits">1</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="cielab_l_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="digits">-1</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="cielab_a_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="round_digits">1</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="cielab_b_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="round_digits">1</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="rgb_green_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="round_digits">1</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">5</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="rgb_blue_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="round_digits">1</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">5</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="rgb_red_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="round_digits">1</property>
+ <property name="digits">3</property>
+ </object>
+ <packing>
+ <property name="left_attach">5</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkListStore" id="mode_store">
+ <columns>
+ <column type="GstyleColorPlaneMode"/>
+ <column type="gchararray"/>
+ </columns>
+ </object>
+</interface>
diff --git a/contrib/gstyle/tests/data/palette.gpl b/contrib/gstyle/tests/data/palette.gpl
new file mode 100644
index 0000000..d8b868b
--- /dev/null
+++ b/contrib/gstyle/tests/data/palette.gpl
@@ -0,0 +1,142 @@
+GIMP Palette
+Name: SVG
+#
+ 0 0 0 black (#000000)
+105 105 105 dimgray (#696969)
+128 128 128 gray (#808080)
+169 169 169 darkgray (#A9A9A9)
+192 192 192 silver (#C0C0C0)
+211 211 211 lightgray (#D3D3D3)
+220 220 220 gainsboro (#DCDCDC)
+245 245 245 whitesmoke (#F5F5F5)
+255 255 255 white (#FFFFFF)
+188 143 143 rosybrown (#BC8F8F)
+205 92 92 indianred (#CD5C5C)
+165 42 42 brown (#A52A2A)
+178 34 34 firebrick (#B22222)
+240 128 128 lightcoral (#F08080)
+128 0 0 maroon (#800000)
+139 0 0 darkred (#8B0000)
+255 0 0 red (#FF0000)
+255 250 250 snow (#FFFAFA)
+255 228 225 mistyrose (#FFE4E1)
+250 128 114 salmon (#FA8072)
+255 99 71 tomato (#FF6347)
+233 150 122 darksalmon (#E9967A)
+255 127 80 coral (#FF7F50)
+255 69 0 orangered (#FF4500)
+255 160 122 lightsalmon (#FFA07A)
+160 82 45 sienna (#A0522D)
+255 245 238 seashell (#FFF5EE)
+210 105 30 chocolate (#D2691E)
+139 69 19 saddlebrown (#8B4513)
+244 164 96 sandybrown (#F4A460)
+255 218 185 peachpuff (#FFDAB9)
+205 133 63 peru (#CD853F)
+250 240 230 linen (#FAF0E6)
+255 228 196 bisque (#FFE4C4)
+255 140 0 darkorange (#FF8C00)
+222 184 135 burlywood (#DEB887)
+210 180 140 tan (#D2B48C)
+250 235 215 antiquewhite (#FAEBD7)
+255 222 173 navajowhite (#FFDEAD)
+255 235 205 blanchedalmond (#FFEBCD)
+255 239 213 papayawhip (#FFEFD5)
+255 228 181 moccasin (#FFE4B5)
+255 165 0 orange (#FFA500)
+245 222 179 wheat (#F5DEB3)
+253 245 230 oldlace (#FDF5E6)
+255 250 240 floralwhite (#FFFAF0)
+184 134 11 darkgoldenrod (#B8860B)
+218 165 32 goldenrod (#DAA520)
+255 248 220 cornsilk (#FFF8DC)
+255 215 0 gold (#FFD700)
+240 230 140 khaki (#F0E68C)
+255 250 205 lemonchiffon (#FFFACD)
+238 232 170 palegoldenrod (#EEE8AA)
+189 183 107 darkkhaki (#BDB76B)
+245 245 220 beige (#F5F5DC)
+250 250 210 lightgoldenrodyellow (#FAFAD2)
+128 128 0 olive (#808000)
+255 255 0 yellow (#FFFF00)
+255 255 224 lightyellow (#FFFFE0)
+255 255 240 ivory (#FFFFF0)
+107 142 35 olivedrab (#6B8E23)
+154 205 50 yellowgreen (#9ACD32)
+ 85 107 47 darkolivegreen (#556B2F)
+173 255 47 greenyellow (#ADFF2F)
+127 255 0 chartreuse (#7FFF00)
+124 252 0 lawngreen (#7CFC00)
+143 188 143 darkseagreen (#8FBC8F)
+ 34 139 34 forestgreen (#228B22)
+ 50 205 50 limegreen (#32CD32)
+144 238 144 lightgreen (#90EE90)
+152 251 152 palegreen (#98FB98)
+ 0 100 0 darkgreen (#006400)
+ 0 128 0 green (#008000)
+ 0 255 0 lime (#00FF00)
+240 255 240 honeydew (#F0FFF0)
+ 46 139 87 seagreen (#2E8B57)
+ 60 179 113 mediumseagreen (#3CB371)
+ 0 255 127 springgreen (#00FF7F)
+245 255 250 mintcream (#F5FFFA)
+ 0 250 154 mediumspringgreen (#00FA9A)
+102 205 170 mediumaquamarine (#66CDAA)
+127 255 212 aquamarine (#7FFFD4)
+ 64 224 208 turquoise (#40E0D0)
+ 32 178 170 lightseagreen (#20B2AA)
+ 72 209 204 mediumturquoise (#48D1CC)
+ 47 79 79 darkslategray (#2F4F4F)
+175 238 238 paleturquoise (#AFEEEE)
+ 0 128 128 teal (#008080)
+ 0 139 139 darkcyan (#008B8B)
+ 0 255 255 cyan (#00FFFF)
+224 255 255 lightcyan (#E0FFFF)
+240 255 255 azure (#F0FFFF)
+ 0 206 209 darkturquoise (#00CED1)
+ 95 158 160 cadetblue (#5F9EA0)
+176 224 230 powderblue (#B0E0E6)
+173 216 230 lightblue (#ADD8E6)
+ 0 191 255 deepskyblue (#00BFFF)
+135 206 235 skyblue (#87CEEB)
+135 206 250 lightskyblue (#87CEFA)
+ 70 130 180 steelblue (#4682B4)
+240 248 255 aliceblue (#F0F8FF)
+ 30 144 255 dodgerblue (#1E90FF)
+112 128 144 slategray (#708090)
+119 136 153 lightslategray (#778899)
+176 196 222 lightsteelblue (#B0C4DE)
+100 149 237 cornflowerblue (#6495ED)
+ 65 105 225 royalblue (#4169E1)
+ 25 25 112 midnightblue (#191970)
+230 230 250 lavender (#E6E6FA)
+ 0 0 128 navy (#000080)
+ 0 0 139 darkblue (#00008B)
+ 0 0 205 mediumblue (#0000CD)
+ 0 0 255 blue (#0000FF)
+248 248 255 ghostwhite (#F8F8FF)
+106 90 205 slateblue (#6A5ACD)
+ 72 61 139 darkslateblue (#483D8B)
+123 104 238 mediumslateblue (#7B68EE)
+147 112 219 mediumpurple (#9370DB)
+138 43 226 blueviolet (#8A2BE2)
+ 75 0 130 indigo (#4B0082)
+153 50 204 darkorchid (#9932CC)
+148 0 211 darkviolet (#9400D3)
+186 85 211 mediumorchid (#BA55D3)
+216 191 216 thistle (#D8BFD8)
+221 160 221 plum (#DDA0DD)
+238 130 238 violet (#EE82EE)
+128 0 128 purple (#800080)
+139 0 139 darkmagenta (#8B008B)
+255 0 255 magenta (#FF00FF)
+218 112 214 orchid (#DA70D6)
+199 21 133 mediumvioletred (#C71585)
+255 20 147 deeppink (#FF1493)
+255 105 180 hotpink (#FF69B4)
+255 240 245 lavenderblush (#FFF0F5)
+219 112 147 palevioletred (#DB7093)
+220 20 60 crimson (#DC143C)
+255 192 203 pink (#FFC0CB)
+255 182 193 lightpink (#FFB6C1)
+102 51 153 rebeccapurple (#663399)
diff --git a/contrib/gstyle/tests/data/palette.xml b/contrib/gstyle/tests/data/palette.xml
new file mode 100644
index 0000000..f62c42d
--- /dev/null
+++ b/contrib/gstyle/tests/data/palette.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+-->
+<palette id="basic" _name="Basic">
+
+ <color name="color_hex6" value="#808080"/>
+ <color name="color_hex3" value="#1aF"/>
+ <color name="color_rgb" value="rgb(100, 200, 50)"/>
+ <color name="color_rgb_percent" value="rgb(10%, 50%, 70%)"/>
+ <color name="color_rgba" value="rgba(0, 10, 70, 1)"/>
+ <color name="color_rgba_percent" value="rgba(10%, 50%, 40%, 0.5)"/>
+ <color name="color_hsl" value="hsl(100, 100%, 50%)"/>
+ <color name="color_hsla" value="hsla(400, 50%, 40%, 0.5)"/>
+ <color name="color_named" value="aliceblue"/>
+
+</palette>
diff --git a/contrib/gstyle/tests/test-gstyle-color-panel.c b/contrib/gstyle/tests/test-gstyle-color-panel.c
new file mode 100644
index 0000000..fd232cf
--- /dev/null
+++ b/contrib/gstyle/tests/test-gstyle-color-panel.c
@@ -0,0 +1,78 @@
+/* test-gstyle-color-panel.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-color-panel.h"
+
+static void
+test_color_panel (void)
+{
+ GtkWidget *window;
+ GtkWidget *box;
+ GstyleColorPanel *color_panel;
+ GstylePaletteWidget *palette_widget;
+ GstylePalette *palette;
+ GFile *file;
+
+ gtk_init (NULL, NULL);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 400,900);
+
+ box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ "expand", TRUE,
+ "spacing", 1,
+ NULL);
+
+ color_panel = g_object_new (GSTYLE_TYPE_COLOR_PANEL, NULL);
+ g_object_ref (color_panel);
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (color_panel));
+
+ palette_widget = gstyle_color_panel_get_palette_widget (color_panel);
+
+ file = g_file_new_for_path (TEST_DATA_DIR"/palette.gpl");
+ palette = gstyle_palette_new_from_file (file, NULL, NULL);
+ gstyle_palette_widget_add (palette_widget, palette);
+ g_object_unref (file);
+
+ file = g_file_new_for_path (TEST_DATA_DIR"/palette.xml");
+ palette = gstyle_palette_new_from_file (file, NULL, NULL);
+ gstyle_palette_widget_add (palette_widget, palette);
+ g_object_unref (file);
+
+ gtk_container_add (GTK_CONTAINER (window), box);
+ g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+ gtk_widget_show_all (window);
+ gtk_main ();
+}
+
+int
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/Gstyle/colorpanel", test_color_panel);
+
+ return g_test_run ();
+}
diff --git a/contrib/gstyle/tests/test-gstyle-color-plane.c b/contrib/gstyle/tests/test-gstyle-color-plane.c
new file mode 100644
index 0000000..811fe16
--- /dev/null
+++ b/contrib/gstyle/tests/test-gstyle-color-plane.c
@@ -0,0 +1,142 @@
+/* test-gstyle-color-plane.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-color-plane.h"
+
+static void
+mode_changed (GstyleColorPlane *self,
+ GtkComboBox *mode_box)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *mode_store;
+ GstyleColorPlaneMode mode;
+
+ g_assert (GSTYLE_IS_COLOR_PLANE (self));
+ g_assert (GTK_IS_COMBO_BOX (mode_box));
+
+ mode_store = gtk_combo_box_get_model (mode_box);
+ if (gtk_combo_box_get_active_iter (mode_box, &iter))
+ {
+ gtk_tree_model_get (mode_store, &iter, 0, &mode, -1);
+ gstyle_color_plane_set_mode (self, mode);
+ }
+}
+
+static void
+test_color_plane (void)
+{
+ g_autoptr (GtkBuilder) builder = NULL;
+ GtkWidget *window;
+ GtkWidget *mode_box;
+ GtkListStore *mode_store;
+ GtkWidget *hue_scale;
+ GtkWidget *saturation_scale;
+ GtkWidget *value_scale;
+ GtkWidget *cielab_l_scale;
+ GtkWidget *cielab_a_scale;
+ GtkWidget *cielab_b_scale;
+ GtkWidget *red_scale;
+ GtkWidget *green_scale;
+ GtkWidget *blue_scale;
+ GtkWidget *box;
+ GstyleColorPlane *plane;
+ GError *error = NULL;
+
+ gtk_init (NULL, NULL);
+ builder = gtk_builder_new ();
+
+ gtk_builder_add_from_file (builder, TEST_DATA_DIR"/gstyle-color-editor.ui", &error);
+ g_assert_no_error (error);
+
+ plane = GSTYLE_COLOR_PLANE (gtk_builder_get_object (builder, "plane"));
+
+ hue_scale = GTK_WIDGET (gtk_builder_get_object (builder, "hsv_h_scale"));
+ gtk_range_set_adjustment (GTK_RANGE (hue_scale),
+ gstyle_color_plane_get_component_adjustment (plane,
GSTYLE_COLOR_COMPONENT_HSV_H));
+
+ saturation_scale = GTK_WIDGET (gtk_builder_get_object (builder, "hsv_s_scale"));
+ gtk_range_set_adjustment (GTK_RANGE (saturation_scale),
+ gstyle_color_plane_get_component_adjustment (plane,
GSTYLE_COLOR_COMPONENT_HSV_S));
+
+ value_scale = GTK_WIDGET (gtk_builder_get_object (builder, "hsv_v_scale"));
+ gtk_range_set_adjustment (GTK_RANGE (value_scale),
+ gstyle_color_plane_get_component_adjustment (plane,
GSTYLE_COLOR_COMPONENT_HSV_V));
+
+ cielab_l_scale = GTK_WIDGET (gtk_builder_get_object (builder, "cielab_l_scale"));
+ gtk_range_set_adjustment (GTK_RANGE (cielab_l_scale),
+ gstyle_color_plane_get_component_adjustment (plane,
GSTYLE_COLOR_COMPONENT_LAB_L));
+
+ cielab_a_scale = GTK_WIDGET (gtk_builder_get_object (builder, "cielab_a_scale"));
+ gtk_range_set_adjustment (GTK_RANGE (cielab_a_scale),
+ gstyle_color_plane_get_component_adjustment (plane,
GSTYLE_COLOR_COMPONENT_LAB_A));
+
+ cielab_b_scale = GTK_WIDGET (gtk_builder_get_object (builder, "cielab_b_scale"));
+ gtk_range_set_adjustment (GTK_RANGE (cielab_b_scale),
+ gstyle_color_plane_get_component_adjustment (plane,
GSTYLE_COLOR_COMPONENT_LAB_A));
+
+ red_scale = GTK_WIDGET (gtk_builder_get_object (builder, "rgb_red_scale"));
+ gtk_range_set_adjustment (GTK_RANGE (red_scale),
+ gstyle_color_plane_get_component_adjustment (plane,
GSTYLE_COLOR_COMPONENT_RGB_RED));
+
+ green_scale = GTK_WIDGET (gtk_builder_get_object (builder, "rgb_green_scale"));
+ gtk_range_set_adjustment (GTK_RANGE (green_scale),
+ gstyle_color_plane_get_component_adjustment (plane,
GSTYLE_COLOR_COMPONENT_RGB_RED));
+
+ blue_scale = GTK_WIDGET (gtk_builder_get_object (builder, "rgb_blue_scale"));
+ gtk_range_set_adjustment (GTK_RANGE (blue_scale),
+ gstyle_color_plane_get_component_adjustment (plane,
GSTYLE_COLOR_COMPONENT_RGB_BLUE));
+
+ mode_box = GTK_WIDGET (gtk_builder_get_object (builder, "mode_box"));
+ mode_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "mode_store"));
+ gtk_list_store_insert_with_values (mode_store, NULL, -1, 0, GSTYLE_COLOR_PLANE_MODE_HUE, 1, "Hsv Hue", -1);
+ gtk_list_store_insert_with_values (mode_store, NULL, -1, 0, GSTYLE_COLOR_PLANE_MODE_SATURATION, 1, "Hsv
Saturation", -1);
+ gtk_list_store_insert_with_values (mode_store, NULL, -1, 0, GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS, 1, "Hsv
Brightness (Value)", -1);
+ gtk_list_store_insert_with_values (mode_store, NULL, -1, 0, GSTYLE_COLOR_PLANE_MODE_CIELAB_L, 1, "CieLab
L*", -1);
+ gtk_list_store_insert_with_values (mode_store, NULL, -1, 0, GSTYLE_COLOR_PLANE_MODE_CIELAB_A, 1, "CieLab
a*", -1);
+ gtk_list_store_insert_with_values (mode_store, NULL, -1, 0, GSTYLE_COLOR_PLANE_MODE_CIELAB_B, 1, "CieLab
b*", -1);
+ gtk_list_store_insert_with_values (mode_store, NULL, -1, 0, GSTYLE_COLOR_PLANE_MODE_RED, 1, "rgb red", -1);
+ gtk_list_store_insert_with_values (mode_store, NULL, -1, 0, GSTYLE_COLOR_PLANE_MODE_GREEN, 1, "rgb green",
-1);
+ gtk_list_store_insert_with_values (mode_store, NULL, -1, 0, GSTYLE_COLOR_PLANE_MODE_BLUE, 1, "rgb blue",
-1);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (mode_box), 0);
+ g_signal_connect_swapped (mode_box, "changed", G_CALLBACK (mode_changed), plane);
+
+ box = GTK_WIDGET (gtk_builder_get_object (builder, "editor_box"));
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_container_add (GTK_CONTAINER (window), box);
+ gtk_window_set_default_size (GTK_WINDOW (window), 400,400);
+ g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+ gtk_widget_show_all (window);
+ gtk_main ();
+}
+
+int
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/Gstyle/colorplane", test_color_plane);
+
+ return g_test_run ();
+}
diff --git a/contrib/gstyle/tests/test-gstyle-color-scale.c b/contrib/gstyle/tests/test-gstyle-color-scale.c
new file mode 100644
index 0000000..43f6544
--- /dev/null
+++ b/contrib/gstyle/tests/test-gstyle-color-scale.c
@@ -0,0 +1,70 @@
+/* test-gstyle-color-scale.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-color-scale.h"
+
+static void
+test_color_scale (void)
+{
+ GtkWidget *window;
+ GtkWidget *box;
+ GstyleColorScale *color_scale;
+ GtkAdjustment *adj;
+
+ gtk_init (NULL, NULL);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 400,100);
+ g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+ box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "expand", TRUE,
+ "spacing", 1,
+ NULL);
+
+ gtk_container_add (GTK_CONTAINER (window), box);
+
+ adj = gtk_adjustment_new (0.0, 0.0, 360.0, 0.1, 1.0, 0.0);
+ color_scale = g_object_new (GSTYLE_TYPE_COLOR_SCALE,
+ "adjustment", adj,
+ "draw-value", FALSE,
+ "expand", TRUE,
+ "valign", GTK_ALIGN_CENTER,
+ "halign", GTK_ALIGN_FILL,
+ NULL);
+
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (color_scale));
+
+ gtk_widget_show_all (window);
+ gtk_main ();
+}
+
+int
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/Gstyle/colorscale", test_color_scale);
+
+ return g_test_run ();
+}
diff --git a/contrib/gstyle/tests/test-gstyle-color-widget.c b/contrib/gstyle/tests/test-gstyle-color-widget.c
new file mode 100644
index 0000000..83c82d1
--- /dev/null
+++ b/contrib/gstyle/tests/test-gstyle-color-widget.c
@@ -0,0 +1,85 @@
+/* test-gstyle-color-widget.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-color-widget.h"
+
+static GstyleColorWidget *
+create_color_swatch (const gchar *color_str)
+{
+ GstyleColor *color;
+ GstyleColorWidget *swatch;
+
+ color = gstyle_color_new_from_string ("test", color_str);
+
+ swatch = g_object_new (GSTYLE_TYPE_COLOR_WIDGET,
+ "halign", GTK_ALIGN_FILL,
+ "color", color,
+ "name-visible", FALSE,
+ "fallback-name-visible", FALSE,
+ NULL);
+
+ return swatch;
+}
+
+static void
+test_color_widget (void)
+{
+ GtkWidget *window;
+ GtkWidget *box;
+ GstyleColorWidget *swatch;
+
+ gtk_init (NULL, NULL);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 200,100);
+ g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+ box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "expand", TRUE,
+ "spacing", 1,
+ NULL);
+
+ gtk_container_add (GTK_CONTAINER (window), box);
+
+ swatch = create_color_swatch ("#5080FF");
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (swatch));
+
+ swatch = create_color_swatch ("#8010A0");
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (swatch));
+
+ swatch = create_color_swatch ("rgba(0, 100, 200, 0.5)");
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (swatch));
+
+ gtk_widget_show_all (window);
+ gtk_main ();
+}
+
+int
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/Gstyle/colorwidget", test_color_widget);
+
+ return g_test_run ();
+}
diff --git a/contrib/gstyle/tests/test-gstyle-color.c b/contrib/gstyle/tests/test-gstyle-color.c
new file mode 100644
index 0000000..4cdaf5b
--- /dev/null
+++ b/contrib/gstyle/tests/test-gstyle-color.c
@@ -0,0 +1,397 @@
+/* test-gstyle-color.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-color.h"
+#include "gstyle-color-component.h"
+#include "gstyle-cielab.h"
+#include "gstyle-color-convert.h"
+#include "gstyle-color-item.h"
+#include "gstyle-xyz.h"
+
+typedef struct
+{
+ gdouble l1;
+ gdouble a1;
+ gdouble b1;
+
+ gdouble l2;
+ gdouble a2;
+ gdouble b2;
+
+ gdouble delta_e;
+} ColorItem;
+
+static ColorItem lab_table[] =
+{
+ { 0, 100, -100, 0, 100, -100, 0.0 },
+ { 0, -100, 100, 0, -100, 100, 0.0 },
+ { 0, 0, 100, 0, 0, 100, 0.0 },
+ { 100, 100, 100, 100, 100, 100, 0.0 },
+ { 100, 0, 0, 100, 0, 0, 0.0 },
+ { 53, 80, 67, 47, 67, 47, 8.372140 },
+ { 10, 20, 30, 10, 20, 31, 0.518452 },
+ { 10, -10, 10, 11, -9, 9, 1.184177 },
+ { 100, -128, 0, 99, -128, 5, 1.817340 },
+ { 75, -50, 0, 70, -50, 5, 4.708821 },
+ { 0 }
+};
+
+/* TODO: use a speific color table with results to compare */
+static void
+test_deltae (void)
+{
+ ColorItem *item;
+ GstyleCielab lab1, lab2;
+ gdouble calc_delta_e, delta_e;
+
+ printf ("\n");
+
+ for (gint n = 0; n < G_N_ELEMENTS (lab_table); ++n)
+ {
+ item = &lab_table[n];
+ lab1.l = item->l1;
+ lab1.a = item->a1;
+ lab1.b = item->b1;
+ lab2.l = item->l2;
+ lab2.a = item->a2;
+ lab2.b = item->b2;
+ delta_e = item->delta_e;
+
+ calc_delta_e = gstyle_color_delta_e (&lab1, &lab2);
+ printf ("lab(%f, %f, %f) vs lab(%f, %f, %f) deltaE (%f): %f\n",
+ lab1.l, lab1.a, lab1.b,
+ lab2.l, lab2.a, lab2.b,
+ delta_e, calc_delta_e);
+
+ g_assert (delta_e > calc_delta_e - 1e-3 && delta_e < calc_delta_e + 1e-3);
+ }
+}
+
+#define RGB_INC (1.0 / 255.0)
+#define RGB_SAMPLES (255.0 * 255.0 * 255.0)
+
+#define HSV_H_INC (1.0 / 360.0)
+#define HSV_SV_INC (1.0 / 100.0)
+#define HSV_SAMPLES (360.0 * 100.0 * 100.0)
+
+#define LAB_L_INC (1.0)
+#define LAB_AB_INC (1.0)
+#define LAB_SAMPLES (100.0 * 256.0 * 256.0)
+
+static void
+delta_rgb ()
+{
+ GdkRGBA src_rgba,dst_rgba;
+ GdkRGBA max_src_r_rgba, max_src_g_rgba, max_src_b_rgba;
+ GdkRGBA max_dst_r_rgba;
+ G_GNUC_UNUSED GdkRGBA max_dst_g_rgba, max_dst_b_rgba ;
+ GstyleXYZ xyz, max_r_xyz, max_g_xyz, max_b_xyz;
+ gdouble r, g, b;
+ GdkRGBA delta_rgba;
+ gdouble dr_max = 0.0;
+ gdouble dg_max = 0.0;
+ gdouble db_max = 0.0;
+ gdouble dr_min = 0.0;
+ gdouble dg_min = 0.0;
+ gdouble db_min = 0.0;
+ gdouble dr_moy = 0.0;
+ gdouble dg_moy = 0.0;
+ gdouble db_moy = 0.0;
+ gdouble start_time, time;
+
+ start_time = g_get_monotonic_time ();
+
+ max_src_r_rgba.alpha = 0.0;
+ max_src_g_rgba.alpha = 0.0;
+ max_src_b_rgba.alpha = 0.0;
+ max_dst_r_rgba.alpha = 0.0;
+ max_dst_g_rgba.alpha = 0.0;
+ max_dst_b_rgba.alpha = 0.0;
+
+ /* rgb to xyz */
+ for (r = 0.0; r <= 1.0; r += RGB_INC)
+ {
+ for (g = 0.0; g <= 1.0; g += RGB_INC)
+ {
+ for (b = 0.0; b <= 1.0; b += RGB_INC)
+ {
+ src_rgba.red = r;
+ src_rgba.green = g;
+ src_rgba.blue = b;
+
+ gstyle_color_convert_rgb_to_xyz (&src_rgba, &xyz);
+ gstyle_color_convert_xyz_to_rgb (&xyz, &dst_rgba);
+
+ delta_rgba.red = src_rgba.red - dst_rgba.red;
+ delta_rgba.green = src_rgba.green - dst_rgba.green;
+ delta_rgba.blue = src_rgba.blue - dst_rgba.blue;
+
+ dr_moy += ABS (delta_rgba.red);
+ dg_moy += ABS (delta_rgba.green);
+ db_moy += ABS (delta_rgba.blue);
+
+ if (ABS (delta_rgba.red) > dr_max)
+ {
+ dr_max = ABS (delta_rgba.red);
+ max_src_r_rgba = src_rgba;
+ max_dst_r_rgba = dst_rgba;
+ max_r_xyz = xyz;
+ }
+
+ if (ABS (delta_rgba.green) > dg_max)
+ {
+ dg_max = ABS (delta_rgba.green);
+ max_src_g_rgba = src_rgba;
+ max_dst_g_rgba = dst_rgba;
+ max_g_xyz = xyz;
+ }
+
+ if (ABS (delta_rgba.blue) > db_max)
+ {
+ db_max = ABS (delta_rgba.blue);
+ max_src_b_rgba = src_rgba;
+ max_dst_b_rgba = dst_rgba;
+ max_b_xyz = xyz;
+ }
+
+ dr_min = MIN (dr_min, ABS (delta_rgba.red));
+ dg_min = MIN (dg_min, ABS (delta_rgba.green));
+ db_min = MIN (db_min, ABS (delta_rgba.blue));
+ }
+ }
+ }
+
+ time = g_get_monotonic_time () - start_time;
+ dr_moy /= 255.0 * 255.0 * 255.0;
+ dg_moy /= 255.0 * 255.0 * 255.0;
+ db_moy /= 255.0 * 255.0 * 255.0;
+
+ printf ("\nRGB -> XYZ -> RGB:\n");
+ printf ("red:\n\tΔmax: %f%% (normalized/255: %f)\n\tΔmin: %f%% (normalized/255: %f)\n\tΔmoy:
%f%%(normalized/255: %f)\n\n",
+ dr_max, dr_max * 255.0, dr_min, dr_min * 255.0, dr_moy, dr_moy * 255.0);
+ printf ("green:\n\tΔmax: %f%% (normalized/255: %f)\n\tΔmin: %f%% (normalized/255: %f)\n\tΔmoy:
%f%%(normalized/255: %f)\n\n",
+ dg_max, dg_max * 255.0, dg_min, dg_min * 255.0, dg_moy, dg_moy * 255.0);
+ printf ("blue:\n\tΔmax: %f%% (normalized/255: %f)\n\tΔmin: %f%% (normalized/255: %f)\n\tΔmoy:
%f%%(normalized/255: %f)\n\n",
+ db_max, db_max * 255.0, db_min, db_min * 255.0, db_moy, db_moy * 255.0);
+
+ printf ("time micro sec: %f (per sample:%f) sec: %f\n\n", time, time / RGB_SAMPLES, time / 1.0e6);
+
+ printf ("max red src rgba:%s dst rgba:%s xyz:(%f, %f, %f)\n",
+ gdk_rgba_to_string (&max_src_r_rgba), gdk_rgba_to_string (&max_dst_r_rgba), max_r_xyz.x,
max_r_xyz.y, max_r_xyz.z);
+ printf ("max green src rgba:%s dst rgba:%s xyz:(%f, %f, %f)\n",
+ gdk_rgba_to_string (&max_src_g_rgba), gdk_rgba_to_string (&max_dst_r_rgba), max_g_xyz.x,
max_g_xyz.y, max_g_xyz.z);
+ printf ("max blue src rgba:%s dst rgba:%s xyz:(%f, %f, %f)\n",
+ gdk_rgba_to_string (&max_src_b_rgba), gdk_rgba_to_string (&max_dst_r_rgba), max_b_xyz.x,
max_b_xyz.y, max_b_xyz.z);
+}
+
+static void
+delta_hsv ()
+{
+ gdouble src_h, src_s, src_v;
+ gdouble dst_h, dst_s, dst_v;
+ GstyleXYZ xyz;
+ gdouble dh = 0.0;
+ gdouble ds = 0.0;
+ gdouble dv = 0.0;
+ gdouble dh_max = 0.0;
+ gdouble ds_max = 0.0;
+ gdouble dv_max = 0.0;
+ gdouble dh_min = 0.0;
+ gdouble ds_min = 0.0;
+ gdouble dv_min = 0.0;
+ gdouble dh_moy = 0.0;
+ gdouble ds_moy = 0.0;
+ gdouble dv_moy = 0.0;
+ gdouble start_time, time;
+ gdouble max_src_hsv_h = 0.0;
+ gdouble max_src_hsv_s = 0.0;
+ gdouble max_src_hsv_v = 0.0;
+ gdouble max_dst_hsv_h = 0.0;
+ gdouble max_dst_hsv_s = 0.0;
+ gdouble max_dst_hsv_v = 0.0;
+
+ start_time = g_get_monotonic_time ();
+
+ /* rgb to xyz */
+ for (src_h = 0.0; src_h <= 1.0; src_h += HSV_H_INC)
+ {
+ for (src_s = 0.0; src_s <= 1.0; src_s += HSV_SV_INC)
+ {
+ for (src_v = 0.0; src_v <= 1.0; src_v += HSV_SV_INC)
+ {
+ gstyle_color_convert_hsv_to_xyz (src_h, src_s, src_v, &xyz);
+ gstyle_color_convert_xyz_to_hsv (&xyz, &dst_h, &dst_s, &dst_v);
+
+ dh = dst_h - src_h;
+ ds = dst_s - src_s;
+ dv = dst_v - src_v;
+
+ dh_moy += ABS (dh);
+ ds_moy += ABS (ds);
+ dv_moy += ABS (dv);
+
+ if (ABS(dh) > dh_max)
+ {
+ dh_max = ABS (dh);
+ max_src_hsv_h = src_h;
+ max_src_hsv_s = src_s;
+ max_src_hsv_v = src_v;
+ max_dst_hsv_h = dst_h;
+ max_dst_hsv_h = dst_s;
+ max_dst_hsv_h = dst_v;
+ }
+
+ if (ABS(ds) > ds_max)
+ {
+ ds_max = ABS (ds);
+ max_src_hsv_h = src_h;
+ max_src_hsv_s = src_s;
+ max_src_hsv_v = src_v;
+ max_dst_hsv_h = dst_h;
+ max_dst_hsv_s = dst_s;
+ max_dst_hsv_v = dst_v;
+ }
+
+ if (ABS(dv) > dv_max)
+ {
+ dv_max = ABS (dv);
+ max_src_hsv_h = src_h;
+ max_src_hsv_s = src_s;
+ max_src_hsv_v = src_v;
+ max_dst_hsv_h = dst_h;
+ max_dst_hsv_s = dst_s;
+ max_dst_hsv_v = dst_v;
+ }
+
+ dh_min = MIN (dh_min, ABS (dh));
+ ds_min = MIN (ds_min, ABS (ds));
+ dv_min = MIN (dv_min, ABS (dv));
+ }
+ }
+ }
+
+ time = g_get_monotonic_time () - start_time;
+ dh_moy /= 360.0 * 100.0 * 100.0;
+ ds_moy /= 255.0 * 100.0 * 100.0;
+ dv_moy /= 255.0 * 100.0 * 100.0;
+
+ printf ("\nHSV -> XYZ -> HSV:\n");
+ printf ("hue:\n\tΔmax: %f%% (norm/360:%f)\n\tΔmin:%f%% (norm/360:%f)\n\tΔmoy:%f%% (norm/360:%f)\n\n",
+ dh_max, dh_max * 360.0, dh_min, dh_min * 360.0, dh_moy, dh_moy * 360.0);
+ printf ("saturation:\n\tΔmax:%f%% (norm/100:%f)\n\tΔmin:%f%% (norm/100:%f)\n\tΔmoy:%f%% (norm/100:%f)\n\n",
+ ds_max, ds_max * 100.0, ds_min, ds_min * 100.0, ds_moy, ds_moy * 100.0);
+ printf ("value:\n\tΔmax:%f%% (norm/100:%f)\n\tΔmin:%f%% (norm/100:%f)\n\tΔmoy:%f%% (norm/100:%f)\n\n",
+ dv_max, dv_min * 100.0, dv_min, dv_min * 100.0, dv_moy, dv_moy * 100.0);
+
+ printf ("time micro sec: %f (per sample:%f) sec: %f\n\n", time, time / HSV_SAMPLES, time / 1.0e6);
+
+ printf ("max hue src hsv(%f,%f,%f) dst hsv(%f,%f,%f)\n",
+ max_src_hsv_h, max_src_hsv_s, max_src_hsv_v,
+ max_dst_hsv_h, max_dst_hsv_s, max_dst_hsv_v);
+}
+
+static void
+delta_lab ()
+{
+ GstyleXYZ xyz;
+ GstyleCielab src_lab;
+ GstyleCielab dst_lab;
+ gdouble dl = 0.0;
+ gdouble da = 0.0;
+ gdouble db = 0.0;
+ gdouble dl_max = 0.0;
+ gdouble da_max = 0.0;
+ gdouble db_max = 0.0;
+ gdouble dl_min = 0.0;
+ gdouble da_min = 0.0;
+ gdouble db_min = 0.0;
+ gdouble dl_moy = 0.0;
+ gdouble da_moy = 0.0;
+ gdouble db_moy = 0.0;
+ gdouble start_time, time;
+
+ start_time = g_get_monotonic_time ();
+
+ /* rgb to xyz */
+ for (src_lab.l = 0.0; src_lab.l <= 100.0; src_lab.l += LAB_L_INC)
+ {
+ for (src_lab.a = -128.0; src_lab.a <= 128.0; src_lab.a += LAB_AB_INC)
+ {
+ for (src_lab.b = -128.0; src_lab.b <= 128.0; src_lab.b += LAB_AB_INC)
+ {
+ gstyle_color_convert_cielab_to_xyz (&src_lab, &xyz);
+ gstyle_color_convert_xyz_to_cielab (&xyz, &dst_lab);
+
+ dl = src_lab.l - dst_lab.l;
+ da = src_lab.a - dst_lab.a;
+ db = src_lab.b - dst_lab.b;
+
+ dl_moy += ABS (dl);
+ da_moy += ABS (da);
+ db_moy += ABS (db);
+
+ dl_max = MAX (dl_max, ABS (dl));
+ da_max = MAX (da_max, ABS (da));
+ db_max = MAX (db_max, ABS (db));
+
+ dl_min = MIN (dl_min, ABS (dl));
+ da_min = MIN (da_min, ABS (da));
+ db_min = MIN (db_min, ABS (db));
+ }
+ }
+ }
+
+ time = g_get_monotonic_time () - start_time;
+ dl_moy /= 100.0 * 256.0 * 256.0;
+ da_moy /= 100.0 * 256.0 * 256.0;
+ db_moy /= 100.0 * 256.0 * 256.0;
+
+ printf ("\nLAB -> XYZ -> LAB:\n");
+ printf ("L* in [0, 100]:\n\tΔl max: %f%%\n\tΔl min:%f%%\n\tΔl moy:%f%%\n\n",
+ dl_max, dl_min, dl_moy);
+ printf ("a* in [-128, +128]:\n\tΔa max: %f%%\n\tΔa min:%f\n\tΔa moy:%f%%\n\n",
+ da_max, da_min, da_moy);
+ printf ("b* in [-128, +128]:\n\tΔb max: %f%%\n\tΔb min:%f%%\n\tΔb moy:%f%%\n\n",
+ db_max, db_min, db_moy);
+
+ printf ("time micro sec: %f (per sample:%f) sec: %f\n\n", time, time / LAB_SAMPLES, time / 1.0e6);
+}
+
+static void
+test_conversion (void)
+{
+ delta_rgb ();
+ delta_hsv ();
+ delta_lab ();
+}
+
+int
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/Gstyle/deltaE", test_deltae);
+ g_test_add_func ("/Gstyle/conversion", test_conversion);
+
+ return g_test_run ();
+}
diff --git a/contrib/gstyle/tests/test-gstyle-palette-widget.c
b/contrib/gstyle/tests/test-gstyle-palette-widget.c
new file mode 100644
index 0000000..296165e
--- /dev/null
+++ b/contrib/gstyle/tests/test-gstyle-palette-widget.c
@@ -0,0 +1,76 @@
+/* test-gstyle-palette-widget.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-palette-widget.h"
+
+static void
+test_palette_widget (void)
+{
+ GtkWidget *window;
+ GtkWidget *box;
+ GstylePaletteWidget *palette_widget;
+ GstylePalette *palette;
+ GFile *file;
+
+ gtk_init (NULL, NULL);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 400,900);
+
+ box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "expand", TRUE,
+ "spacing", 1,
+ NULL);
+
+ palette_widget = g_object_new (GSTYLE_TYPE_PALETTE_WIDGET, NULL);
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (palette_widget));
+
+ file = g_file_new_for_path (TEST_DATA_DIR"/palette.gpl");
+ palette = gstyle_palette_new_from_file (file, NULL, NULL);
+ gstyle_palette_widget_add (palette_widget, palette);
+ g_object_unref (file);
+
+ file = g_file_new_for_path (TEST_DATA_DIR"/palette.xml");
+ palette = gstyle_palette_new_from_file (file, NULL, NULL);
+ gstyle_palette_widget_add (palette_widget, palette);
+ g_object_unref (file);
+
+ gstyle_palette_widget_show_palette (palette_widget, palette);
+
+ gtk_container_add (GTK_CONTAINER (window), box);
+ g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+ gtk_widget_show_all (window);
+ gtk_main ();
+}
+
+int
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/Gstyle/palettewidget", test_palette_widget);
+
+ return g_test_run ();
+}
diff --git a/contrib/gstyle/tests/test-gstyle-palette.c b/contrib/gstyle/tests/test-gstyle-palette.c
new file mode 100644
index 0000000..2df7078
--- /dev/null
+++ b/contrib/gstyle/tests/test-gstyle-palette.c
@@ -0,0 +1,74 @@
+/* test-gstyle-palette.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-palette.h"
+
+static GstylePalette *
+load_palette (gchar *name)
+{
+ GstylePalette *palette;
+ g_autoptr (GFile) file = NULL;
+ g_autofree gchar *uri = NULL;
+ g_autofree gchar *path;
+ GError *error = NULL;
+ GCancellable *cancellable = NULL;
+
+ path = g_strdup_printf (TEST_DATA_DIR"/%s", name);
+ file = g_file_new_for_path (path);
+ palette = gstyle_palette_new_from_file (file, cancellable, &error);
+ if (palette == NULL)
+ printf ("error: %s\n", error->message);
+
+ uri = g_file_get_uri (file);
+
+ printf ("Palette:\n\turi:'%s'\n\tname:'%s'\n\tid:'%s'\n\tnb colors:%i\n",
+ uri,
+ gstyle_palette_get_name (palette),
+ gstyle_palette_get_id (palette),
+ gstyle_palette_get_len (palette));
+
+ return palette;
+}
+
+static void
+test_palette (void)
+{
+ GstylePalette *palette;
+
+ printf ("\n");
+ palette = load_palette ("palette.xml");
+ g_object_unref (palette);
+
+ palette = load_palette ("palette.gpl");
+ g_object_unref (palette);
+}
+
+int
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/Gstyle/palette", test_palette);
+
+ return g_test_run ();
+}
diff --git a/contrib/gstyle/tests/test-gstyle-parse.c b/contrib/gstyle/tests/test-gstyle-parse.c
new file mode 100644
index 0000000..9db0643
--- /dev/null
+++ b/contrib/gstyle/tests/test-gstyle-parse.c
@@ -0,0 +1,152 @@
+/* test-gstyle-parse.c
+ *
+ * Copyright (C) 2016 sebastien lafargue <slafargue gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "gstyle-color.h"
+#include "gstyle-color-convert.h"
+#include "gstyle-color-item.h"
+
+typedef struct
+{
+ gchar *rgb;
+
+ gdouble h;
+ gdouble s;
+ gdouble l;
+ gdouble a;
+} ColorItem;
+
+static ColorItem rgba_table[] =
+{
+ { "#000000", 0, 0, 0, 1 },
+ { "#102030", 16, 32, 48, 1 },
+ { "#FFFFFF", 255, 255, 255, 1 },
+ { "#808080", 128, 128, 128, 1 },
+ { "#1aF", 17, 170, 255, 1 },
+ { "rgb(100, 200, 50)", 100, 200, 50, 1 },
+ { "rgb(10%, 50%, 70%)", 26, 128, 179, 1 },
+ { "rgba(10%, 50%, 40%, 0.5)", 26, 128, 102, 0.5 },
+ { "rgba(0, 10, 70, 1)", 0, 10, 70, 1 },
+ { "hsl(100, 100%, 50%)", 85, 255, 0, 1 },
+ { "hsl(250, 50%, 70%)", 153, 140, 217, 1 },
+ { "hsla(40, 50%, 40%, 0.5)", 153, 119, 51, 0.5 },
+ { "hsla(10, 10%, 70%, 1)", 186, 173, 171, 1 },
+ { "aliceblue", 240, 248, 255, 1 },
+ { "darkgray", 169, 169, 169, 1 },
+ { "peru", 205, 133, 63, 1 },
+ { 0 }
+};
+
+static void
+test_parse_text (void)
+{
+ GPtrArray *items;
+
+ static const gchar *text = "line-background=\"rgba(235,202,210,.4)\"\n"
+ "foreground=\"rgba(100%, 50%, 25%,.4)\"\n"
+ "color: #8d9091;\n"
+ "color: #123;\n"
+ "background-color: hsl(65, 70%, 72%);\n"
+ "text-shadow: 0 1px black;";
+
+ printf ("\n");
+ items = gstyle_color_parse (text);
+ for (gint i = 0; i < items->len; ++i)
+ {
+ gchar *str;
+ const GstyleColor *color;
+ GstyleColorItem *item;
+ guint start, len;
+
+ item = g_ptr_array_index (items, i);
+ color = gstyle_color_item_get_color (item);
+ start = gstyle_color_item_get_start (item);
+ len = gstyle_color_item_get_len (item);
+ str = gstyle_color_to_string ((GstyleColor *)color, GSTYLE_COLOR_KIND_ORIGINAL);
+
+ printf ("item(%i,%i): '%s'\n", start, len, str);
+ g_free (str);
+ }
+
+ g_ptr_array_free (items, TRUE);
+}
+
+static void
+test_parse_string (void)
+{
+ ColorItem *item;
+ GstyleColor *color;
+ GdkRGBA rgba;
+ GstyleColorKind kind;
+ GstyleCielab lab;
+
+ printf ("\n");
+ for (item = rgba_table; item->rgb != NULL; item++)
+ {
+ g_autofree gchar *str_hex3 = NULL;
+ g_autofree gchar *str_hex6 = NULL;
+ g_autofree gchar *str_rgba = NULL;
+ g_autofree gchar *str_rgba_percent = NULL;
+ g_autofree gchar *str_hsla = NULL;
+ g_autofree gchar *str_original = NULL;
+ g_autofree gchar *dst_str = NULL;
+
+ color = gstyle_color_new_from_string (NULL, item->rgb);
+
+ str_hex3 = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_RGB_HEX3);
+ str_hex6 = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_RGB_HEX6);
+ str_rgba = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_RGBA);
+ str_rgba_percent = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_RGBA_PERCENT);
+ str_hsla = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_HSLA);
+ str_original = gstyle_color_to_string (color, GSTYLE_COLOR_KIND_ORIGINAL);
+
+ gstyle_color_fill_rgba (color, &rgba);
+ gstyle_color_convert_rgb_to_cielab (&rgba, &lab);
+
+ kind = gstyle_color_get_kind (color);
+ dst_str = gstyle_color_to_string (color, kind);
+
+ printf ("dst:'%s'\n", dst_str);
+
+
+ printf ("\n----- '%s': rgba: kind:%i\n%s\n%s\n%s\n%s\n%s\n Original: %s\n",
+ item->rgb, kind,
+ str_hex3, str_hex6, str_rgba, str_rgba_percent, str_hsla, str_original);
+
+ printf ("lab : L=%.3f a=%.3f b=%.3f\n", lab.l, lab.a, lab.b);
+
+ g_assert (g_ascii_strcasecmp (item->rgb, dst_str) == 0);
+ g_object_unref (color);
+ printf ("\n");
+ }
+}
+
+int
+main (gint argc,
+ gchar *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/Gstyle/parse_string", test_parse_string);
+ g_test_add_func ("/Gstyle/parse_text", test_parse_text);
+
+ return g_test_run ();
+}
diff --git a/contrib/gstyle/theme/gstyle.css b/contrib/gstyle/theme/gstyle.css
new file mode 100644
index 0000000..d3fa24d
--- /dev/null
+++ b/contrib/gstyle/theme/gstyle.css
@@ -0,0 +1,120 @@
+/* GstyleColorScale */
+
+gstylecolorscale {
+ padding: 12px;
+ background-clip: content-box;
+}
+
+gstylecolorscale highlight {
+ border: none;
+ padding: 0px;
+ margin: 0px;
+ background: transparent;
+}
+
+gstylecolorscale trough {
+ padding: 0px;
+ margin: 0px;
+ background: transparent;
+ border: none;
+ border-radius: 0px;
+ outline-offset: 6px;
+ -gtk-outline-radius: 0px;
+}
+
+gstylecolorscale contents {
+ border: solid white 1px;
+ padding: 0px;
+ margin: 0px;
+ background: transparent;
+}
+
+gstylecolorscale.horizontal contents{
+ min-height: 20px;
+}
+
+gstylecolorscale.vertical contents{
+ min-width: 20px;
+}
+
+gstylecolorscale.horizontal slider {
+ padding: 0px;
+ margin: -12px;
+ min-width: 0px;
+ min-height: 44px;
+ border: none;
+ box-shadow: none;
+ background-position: center;
+ background-size: auto 28px;
+ background-repeat: no-repeat;
+}
+
+gstylecolorscale.vertical slider {
+ padding: 0px;
+ margin: -12px;
+ min-width: 44px;
+ min-height: 0px;
+ border: none;
+ box-shadow: none;
+ background-position: center;
+ background-size: 28px auto;
+ background-repeat: no-repeat;
+}
+
+gstylecolorscale.horizontal slider {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical 2 png"));
+}
+gstylecolorscale.horizontal slider:backdrop {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical-backdrop.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical-backdrop 2 png"));
+}
+gstylecolorscale.horizontal slider:hover {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical-hover.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical-hover 2 png"));
+}
+gstylecolorscale.horizontal slider:disabled {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical-insensitive.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical-insensitive 2 png"));
+}
+gstylecolorscale.horizontal slider:backdrop:disabled {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical-backdrop-insensitive.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical-backdrop-insensitive 2 png"));
+}
+gstylecolorscale.horizontal slider:active {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical-active.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-vertical-active 2 png"));
+}
+
+gstylecolorscale.vertical slider {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal 2 png"));
+}
+gstylecolorscale.vertical slider:backdrop {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal-backdrop.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal-backdrop 2 png"));
+}
+gstylecolorscale.vertical slider:hover {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal-hover.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal-hover 2 png"));
+}
+gstylecolorscale.vertical slider:disabled {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal-insensitive.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal-insensitive 2 png"));
+}
+gstylecolorscale.vertical slider:backdrop:disabled {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal-backdrop-insensitive.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal-backdrop-insensitive 2 png"));
+}
+gstylecolorscale.vertical slider:active {
+ background-image:
-gtk-scaled(url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal-active.png"),
url("resource:///org/gnome/libgstyle/colorscale/color-scale-slider-horizontal-active 2 png"));
+}
+
+/* GstyleColorWidget */
+
+gstylecolorwidget label {
+ padding: 12px;
+}
+
+gstylecolorwidget {
+ background-color: white;
+}
+
+/* GstyleColorPanel */
+
+gstylecolorpanel revealer button.toggle {
+ margin: 6px;
+}
+
+gstyleslidein.shade {
+ color: rgba(77, 78, 83, 0.8);
+}
diff --git a/contrib/gstyle/ui/gstyle-color-panel.ui b/contrib/gstyle/ui/gstyle-color-panel.ui
new file mode 100644
index 0000000..66b7591
--- /dev/null
+++ b/contrib/gstyle/ui/gstyle-color-panel.ui
@@ -0,0 +1,1011 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <template class="GstyleColorPanel" parent="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GstyleSlidein" id="prefs_slidein">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="direction-type">right</property>
+ <property name="slide-fraction">1.0</property>
+ <property name="interpolate-size">true</property>
+ <property name="slide-margin">30</property>
+ <property name="duration">500</property>
+ <property name="hexpand">False</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkPaned">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="wide-handle">1</property>
+ <property name="orientation">vertical</property>
+ <property name="position">250</property>
+ <child>
+ <object class="GtkGrid">
+ <property name="name">editor_grid</property>
+ <property name="visible">1</property>
+ <property name="vexpand">False</property>
+ <property name="valign">fill</property>
+ <child>
+ <object class="GstyleColorScale" id="ref_scale">
+ <property name="kind">custom-data</property>
+ <property name="visible">1</property>
+ <property name="inverted">1</property>
+ <property name="can_focus">1</property>
+ <property name="halign">center</property>
+ <property name="orientation">vertical</property>
+ <property name="round_digits">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorPlane" id="color_plane">
+ <property name="width_request">150</property>
+ <property name="height_request">150</property>
+ <property name="expand">1</property>
+ <property name="halign">fill</property>
+ <property name="visible">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="swatchs_box">
+ <property name="name">swatchs_box</property>
+ <property name="visible">1</property>
+ <property name="hexpand">1</property>
+ <property name="homogeneous">1</property>
+ <child>
+ <object class="GstyleColorWidget" id="new_swatch">
+ <property name="name">new_swatch</property>
+ <property name="visible">1</property>
+ <property name="vexpand">0</property>
+ <property name="can_focus">1</property>
+ <property name="receives_default">1</property>
+ </object>
+ </child>
+ <child>
+ <object class="GstyleColorWidget" id="old_swatch">
+ <property name="name">old_swatch</property>
+ <property name="name-visible">0</property>
+ <property name="fallback-name-visible">0</property>
+ <property name="visible">1</property>
+ <property name="vexpand">0</property>
+ <property name="can_focus">1</property>
+ <property name="receives_default">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="picker_button">
+ <property name="name">picker_button</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <style>
+ <class name="flat"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon-name">eyedropper-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorScale" id="alpha_scale">
+ <property name="name">alpha_scale</property>
+ <property name="kind">alpha</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="round_digits">1</property>
+ <property name="hexpand">1</property>
+ <property name="halign">fill</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="paned_box">
+ <property name="visible">1</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="components_box1">
+ <property name="orientation">vertical</property>
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkBox" id="components_controls">
+ <property name="name">components_controls</property>
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkToggleButton" id="components_toggle">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">Color Components</property>
+ </object>
+ <packing>
+ <property name="expand">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="components_prefs_button">
+ <property name="name">components_prefs_button</property>
+ <property name="action-name">gstyle-pages-prefs.toggle-components-page</property>
+ <property name="action-target">true</property>
+ <property name="visible">1</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">open-menu-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ <property name="expand">0</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <style>
+ <class name="reveal-bar"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GstyleRevealer" id="scale_reveal">
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkBox" id="scale_box">
+ <property name="name">scale_box</property>
+ <property name="visible">1</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkGrid" id="hsv_grid">
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkToggleButton" id="hsv_h_toggle">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">H</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="hsv_s_toggle">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">S</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="hsv_v_toggle">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">V</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorScale" id="hsv_h_scale">
+ <property name="kind">custom-data</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="hexpand">1</property>
+ <property name="round_digits">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorScale" id="hsv_s_scale">
+ <property name="kind">custom-data</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="hexpand">1</property>
+ <property name="round_digits">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorScale" id="hsv_v_scale">
+ <property name="kind">custom-data</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="hexpand">1</property>
+ <property name="round_digits">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="hsv_h_spin">
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="hexpand">0</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="width-chars">6</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="hsv_s_spin">
+ <property name="visible">1</property>
+ <property name="hexpand">0</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="width-chars">6</property>
+ <property name="can_focus">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="hsv_v_spin">
+ <property name="visible">1</property>
+ <property name="hexpand">0</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="width-chars">6</property>
+ <property name="can_focus">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkGrid" id="lab_grid">
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkToggleButton" id="lab_l_toggle">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">L*</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="lab_a_toggle">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">a*</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="lab_b_toggle">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">b*</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorScale" id="lab_l_scale">
+ <property name="kind">custom-data</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="hexpand">1</property>
+ <property name="round_digits">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorScale" id="lab_a_scale">
+ <property name="kind">custom-data</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="hexpand">1</property>
+ <property name="round_digits">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorScale" id="lab_b_scale">
+ <property name="kind">custom-data</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="hexpand">1</property>
+ <property name="round_digits">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="lab_l_spin">
+ <property name="visible">1</property>
+ <property name="hexpand">0</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="width-chars">6</property>
+ <property name="can_focus">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="lab_a_spin">
+ <property name="visible">1</property>
+ <property name="hexpand">0</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="width-chars">6</property>
+ <property name="can_focus">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="lab_b_spin">
+ <property name="visible">1</property>
+ <property name="hexpand">0</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="width-chars">6</property>
+ <property name="can_focus">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="rgb_grid">
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkToggleButton" id="rgb_red_toggle">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">R</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="rgb_green_toggle">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">G</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="rgb_blue_toggle">
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">B</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorScale" id="rgb_red_scale">
+ <property name="kind">custom-data</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="hexpand">1</property>
+ <property name="round_digits">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorScale" id="rgb_green_scale">
+ <property name="kind">custom-data</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="hexpand">1</property>
+ <property name="round_digits">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GstyleColorScale" id="rgb_blue_scale">
+ <property name="kind">custom-data</property>
+ <property name="visible">1</property>
+ <property name="can_focus">1</property>
+ <property name="hexpand">1</property>
+ <property name="round_digits">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="rgb_red_spin">
+ <property name="visible">1</property>
+ <property name="hexpand">0</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="width-chars">6</property>
+ <property name="can_focus">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="rgb_green_spin">
+ <property name="visible">1</property>
+ <property name="hexpand">0</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="width-chars">6</property>
+ <property name="can_focus">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="rgb_blue_spin">
+ <property name="visible">1</property>
+ <property name="hexpand">0</property>
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="width-chars">6</property>
+ <property name="can_focus">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="string_box1">
+ <property name="orientation">vertical</property>
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkBox" id="strings_controls">
+ <property name="name">strings_controls</property>
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkToggleButton" id="strings_toggle">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="visible">1</property>
+ <property name="label" translatable="yes">Color strings</property>
+ </object>
+ <packing>
+ <property name="expand">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="color_strings_prefs_button">
+ <property name="name">color_strings_prefs_button</property>
+ <property
name="action-name">gstyle-pages-prefs.toggle-colorstrings-page</property>
+ <property name="action-target">true</property>
+ <property name="visible">1</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">open-menu-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ <property name="expand">0</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <style>
+ <class name="reveal-bar"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GstyleRevealer" id="string_reveal">
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkBox" id="strings_box">
+ <property name="name">strings_box</property>
+ <property name="orientation">vertical</property>
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkSearchEntry" id="search_color_entry">
+ <property name="name">search_color_entry</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="primary_icon_name">edit-find-symbolic</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">1</property>
+ <property name="name">string_grid</property>
+ <child>
+ <object class="GtkLabel" id="hex3_label">
+ <property name="visible">1</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">HEX3</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="hex6_label">
+ <property name="visible">1</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">HEX6</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="rgb_label">
+ <property name="visible">1</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">RGB</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="rgba_label">
+ <property name="visible">1</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">RGBA</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="hsl_label">
+ <property name="visible">1</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">HSL</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="hsla_label">
+ <property name="visible">1</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">HSLA</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="res_hex3_label">
+ <property name="visible">1</property>
+ <property name="halign">fill</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="selectable">1</property>
+ <style>
+ <class name="selectable"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="res_hex6_label">
+ <property name="visible">1</property>
+ <property name="halign">fill</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="selectable">1</property>
+ <style>
+ <class name="selectable"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="res_rgb_label">
+ <property name="visible">1</property>
+ <property name="halign">fill</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="selectable">1</property>
+ <style>
+ <class name="selectable"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="res_rgba_label">
+ <property name="visible">1</property>
+ <property name="halign">fill</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="selectable">1</property>
+ <style>
+ <class name="selectable"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="res_hsl_label">
+ <property name="visible">1</property>
+ <property name="halign">fill</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="selectable">1</property>
+ <style>
+ <class name="selectable"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="res_hsla_label">
+ <property name="visible">1</property>
+ <property name="halign">fill</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="selectable">1</property>
+ <style>
+ <class name="selectable"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="palette_box1">
+ <property name="orientation">vertical</property>
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkBox" id="palette_controls">
+ <property name="name">palette_controls</property>
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkToggleButton" id="palette_toggle">
+ <property name="name">palette_toggle</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width-chars">20</property>
+ <property name="ellipsize">end</property>
+ <property name="label" translatable="yes">Palettes</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="palettes_list_prefs_button">
+ <property name="name">palettes_list_prefs_button</property>
+ <property
name="action-name">gstyle-pages-prefs.toggle-paletteslist-page</property>
+ <property name="action-target">true</property>
+ <property name="visible">1</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">applications-graphics-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="palettes_prefs_button">
+ <property name="name">palettes_prefs_button</property>
+ <property name="action-name">gstyle-pages-prefs.toggle-palettes-page</property>
+ <property name="action-target">true</property>
+ <property name="visible">1</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">open-menu-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ <property name="expand">0</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <style>
+ <class name="reveal-bar"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GstyleRevealer" id="palette_reveal">
+ <property name="visible">1</property>
+ <child>
+ <object class="GstylePaletteWidget" id="palette_widget">
+ <property name="visible">1</property>
+ <property name="expand">true</property>
+ <property name="placeholder">palette_widget_placeholder</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="expand_box">
+ <property name="visible">1</property>
+ <property name="orientation">vertical</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">1</property>
+ <property name="shrink">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="slide">
+ <object class="GtkStack" id="prefs_stack">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="interpolate_size">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup" id="size_toggle">
+ <widgets>
+ <widget name="hsv_h_toggle"/>
+ <widget name="hsv_s_toggle"/>
+ <widget name="hsv_v_toggle"/>
+ <widget name="lab_l_toggle"/>
+ <widget name="lab_a_toggle"/>
+ <widget name="lab_b_toggle"/>
+ <widget name="rgb_red_toggle"/>
+ <widget name="rgb_green_toggle"/>
+ <widget name="rgb_blue_toggle"/>
+ </widgets>
+ </object>
+ <object class="GtkBox" id="palette_widget_placeholder">
+ <property name="expand">true</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">applications-graphics-symbolic</property>
+ <property name="pixel-size">64</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">No open palettes</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <attributes>
+ <attribute name="scale" value="2.0"/>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Load or generate a palette by using the
preferences</property>
+ <property name="wrap">true</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ <object class="GtkPopover" id="palette_list_popover">
+ <property name="focus-on-click">false</property>
+ <property name="name">palette_popover</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="min-content-width">150</property>
+ <property name="max-content-width">400</property>
+ <property name="max-content-height">400</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkListBox" id="palette_list">
+ <property name="selection-mode">browse</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkPopover" id="search_strings_popover">
+ <property name="focus-on-click">false</property>
+ <property name="name">search_strings_popover</property>
+ <property name="width-request">300</property>
+ <property name="expand">0</property>
+ <property name="position">left</property>
+ <property name="modal">0</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="min-content-height">47</property>
+ <property name="max-content-width">400</property>
+ <property name="max-content-height">400</property>
+ <property name="expand">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkListBox" id="search_strings_list">
+ <property name="selection-mode">browse</property>
+ <property name="expand">true</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/contrib/gstyle/ui/gstyle-palette-widget.ui b/contrib/gstyle/ui/gstyle-palette-widget.ui
new file mode 100644
index 0000000..05f4171
--- /dev/null
+++ b/contrib/gstyle/ui/gstyle-palette-widget.ui
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.18"/>
+ <template class="GstylePaletteWidget" parent="GtkBin">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="vexpand">True</property>
+ <child>
+ <object class="GtkStack" id="view_stack">
+ <property name="visible">True</property>
+ <property name="expand">true</property>
+ <property name="vhomogeneous">false</property>
+ <child>
+ <object class="GtkScrolledWindow" id="list_scrolled_window">
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="visible">true</property>
+ <property name="expand">true</property>
+ <child>
+ <object class="GtkViewport" id="list_viewport">
+ <property name="expand">False</property>
+ <property name="valign">start</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkListBox" id="listbox">
+ <property name="name">palette_listbox</property>
+ <property name="visible">True</property>
+ <property name="expand">False</property>
+ <property name="valign">start</property>
+ <property name="selection-mode">single</property>
+ <property name="activate-on-single-click">0</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="name">list</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="flow_scrolled_window">
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="visible">true</property>
+ <property name="expand">true</property>
+ <child>
+ <object class="GtkViewport" id="flow_viewport">
+ <property name="expand">False</property>
+ <property name="valign">start</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkFlowBox" id="flowbox">
+ <property name="name">palette_flowbox</property>
+ <property name="visible">True</property>
+ <property name="selection-mode">single</property>
+ <property name="min-children-per-line">2</property>
+ <property name="max-children-per-line">10</property>
+ <property name="column-spacing">0</property>
+ <property name="row-spacing">0</property>
+ <property name="homogeneous">true</property>
+ <property name="expand">False</property>
+ <property name="valign">start</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="name">flow</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="placeholder_box">
+ <property name="visible">True</property>
+ <property name="expand">true</property>
+ <property name="valign">center</property>
+ <property name="can_focus">False</property>
+ <property name="margin">6</property>
+ </object>
+ <packing>
+ <property name="name">placeholder</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkPopover" id="palette_popover">
+ <property name="focus-on-click">false</property>
+ <property name="name">palette_popover</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="min-content-width">150</property>
+ <property name="max-content-width">300</property>
+ <property name="max-content-height">600</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkListBox" id="palette_list">
+ <property name="selection-mode">browse</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 84b0fef..98798a6 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -4,6 +4,10 @@ contrib/egg/egg-animation.c
contrib/egg/egg-date-time.c
contrib/egg/egg-file-chooser-entry.c
contrib/egg/egg-search-bar.c
+contrib/gstyle/gstyle-palette.c
+contrib/gstyle/gstyle-color-panel.c
+contrib/gstyle/gstyle-color-plane.c
+contrib/gstyle/gstyle-palette-widget.c
contrib/pnl/pnl-animation.c
contrib/search/trie.c
contrib/tmpl/tmpl-parser.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]