From f3578d35758ac47991806233ceba8d566160260b Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 24 Dec 2020 17:29:08 +0100
Subject: Added a way to retrieve RGBA colors from the current GTK style.

---
 plugins/pychrysalide/gtkext/easygtk.c |  85 ++++++++++++++++++++++++
 src/gtkext/easygtk.c                  | 117 ++++++++++++++++++++++++++++++++++
 src/gtkext/easygtk.h                  |   5 ++
 3 files changed, 207 insertions(+)

diff --git a/plugins/pychrysalide/gtkext/easygtk.c b/plugins/pychrysalide/gtkext/easygtk.c
index fe57c7e..a489cd1 100644
--- a/plugins/pychrysalide/gtkext/easygtk.c
+++ b/plugins/pychrysalide/gtkext/easygtk.c
@@ -41,6 +41,9 @@
     " useful features GTK is missing."
 
 
+/* Identifie la couleur de base associée à un style GTK. */
+static PyObject *py_easygtk_get_color_from_style(PyObject *, PyObject *);
+
 /* Détermine l'indice d'un composant dans un conteneur GTK. */
 static PyObject *py_easygtk_find_contained_child_index(PyObject *, PyObject *);
 
@@ -54,6 +57,87 @@ static PyObject *py_easygtk_get_nth_contained_child(PyObject *, PyObject *);
 *  Paramètres  : self = NULL car méthode statique.                            *
 *                args = paramètres à transmettre à l'appel natif.             *
 *                                                                             *
+*  Description : Identifie la couleur de base associée à un style GTK.        *
+*                                                                             *
+*  Retour      : Bilan présumé de l'opération.                                *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_easygtk_get_color_from_style(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Désignation à retourner     */
+    const char *class;                      /* Classe de style GTK         */
+    int background;                         /* Nature du traitement        */
+    int ret;                                /* Bilan de lecture des args.  */
+    GdkRGBA color;                          /* Couleur obtenue             */
+    bool status;                            /* Bilan de la récupération    */
+    PyObject *gdk_mod;                      /* Module Python Gdk           */
+    PyObject *rgba_type;                    /* Classe "GtkRGBA"            */
+    PyObject *rgba_args;                    /* Arguments pour l'appel      */
+
+#define EASYGTK_GET_COLOR_FROM_STYLE_METHOD PYTHON_METHOD_DEF     \
+(                                                                       \
+    get_color_from_style, "cls, background, /",                  \
+    METH_VARARGS | METH_STATIC, py_easygtk,                             \
+    "Find the index of a given child widget inside a GTK container"     \
+    " children.\n"                                                      \
+    "\n"                                                                \
+    "The *containter* argument must be a Gtk.Container instance and"    \
+    " *child* a Gtk.Widget instance.\n"                                 \
+    "\n"                                                                \
+    "The result is the found index or -1 in case of error."             \
+)
+
+    ret = PyArg_ParseTuple(args, "sp", &class, &background);
+    if (!ret) return NULL;
+
+    status = get_color_from_style(class, background, &color);
+
+    if (status)
+    {
+        gdk_mod = PyImport_ImportModule("gi.repository.Gdk");
+
+        if (gdk_mod == NULL)
+        {
+            PyErr_SetString(PyExc_TypeError, "unable to find the Gtk Python module");
+            goto done;
+        }
+
+        rgba_type = PyObject_GetAttrString(gdk_mod, "RGBA");
+
+        Py_DECREF(gdk_mod);
+
+        rgba_args = PyTuple_New(4);
+        PyTuple_SetItem(rgba_args, 0, PyFloat_FromDouble(color.red));
+        PyTuple_SetItem(rgba_args, 1, PyFloat_FromDouble(color.green));
+        PyTuple_SetItem(rgba_args, 2, PyFloat_FromDouble(color.blue));
+        PyTuple_SetItem(rgba_args, 3, PyFloat_FromDouble(color.alpha));
+
+        result = PyObject_CallObject(rgba_type, rgba_args);
+
+        Py_DECREF(rgba_args);
+
+    }
+
+    else
+    {
+ done:
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = NULL car méthode statique.                            *
+*                args = paramètres à transmettre à l'appel natif.             *
+*                                                                             *
 *  Description : Détermine l'indice d'un composant dans un conteneur GTK.     *
 *                                                                             *
 *  Retour      : Indice du composant dans le conteneur ou -1 si non trouvé.   *
@@ -165,6 +249,7 @@ static PyObject *py_easygtk_get_nth_contained_child(PyObject *self, PyObject *ar
 PyTypeObject *get_python_easygtk_type(void)
 {
     static PyMethodDef py_easygtk_methods[] = {
+        EASYGTK_GET_COLOR_FROM_STYLE_METHOD,
         EASYGTK_FIND_CONTAINED_CHILD_INDEX_METHOD,
         EASYGTK_GET_NTH_CONTAINED_CHILD_METHOD,
         { NULL }
diff --git a/src/gtkext/easygtk.c b/src/gtkext/easygtk.c
index 6e42556..3a17a72 100644
--- a/src/gtkext/easygtk.c
+++ b/src/gtkext/easygtk.c
@@ -25,6 +25,7 @@
 
 
 #include <assert.h>
+#include <stdint.h>
 
 
 #include "support.h"
@@ -757,9 +758,125 @@ GtkWidget *qck_create_menu_separator(void)
 
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : class      = classe de style à appliquer.                    *
+*                background = indique la nature de la couleur à relever.      *
+*                color      = couleur associée au style indiqué. [OUT]        *
+*                                                                             *
+*  Description : Identifie la couleur de base associée à un style GTK.        *
+*                                                                             *
+*  Retour      : Bilan présumé de l'opération.                                *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool get_color_from_style(const char *class, bool background, GdkRGBA *color)
+{
+    bool result;                            /* Bilan à retourner           */
+    GtkWidget *area;                        /* Composant pour le contexte  */
+    GtkStyleContext *context;               /* Contexte de style GTK       */
+    cairo_surface_t *surface;               /* Surface de dessin temporaire*/
+    cairo_t *cr;                            /* Pinceau pour le dessin      */
+    uint32_t *pixel;                        /* Valeurs pour un pixel choisi*/
+    int a;                                  /* Valeur du canal alpha       */
+    int r;                                  /* Valeur du canal rouge       */
+    int g;                                  /* Valeur du canal vert        */
+    int b;                                  /* Valeur du canal bleu        */
+
+    result = false;
+
+    /* Mise en place de l'environnement */
+
+    area = gtk_drawing_area_new();
+    g_object_ref_sink(G_OBJECT(area));
+
+    context = gtk_widget_get_style_context (area);
+
+    surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 20, 20);
+
+    if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
+        goto empty_surface;
+
+    cr = cairo_create(surface);
+
+    if (cairo_status(cr) != CAIRO_STATUS_SUCCESS)
+        goto bad_cairo;
+
+    /* Dessin */
+
+    gtk_style_context_add_class(context, class);
+
+    if (background)
+        gtk_render_background(context, cr, 0, 0, 20, 20);
+    else
+        gtk_render_line(context, cr, -0.5, -0.5, 19.5, 19.5);
 
+    cairo_surface_flush(surface);
 
+    /* Récupération du pixel (0 ; 0) */
 
+    pixel = (uint32_t *)cairo_image_surface_get_data(surface);
+
+    /* Récupération des valeurs de couleur */
+
+    a = (*pixel & 0xff000000) >> 24;
+    r = (*pixel & 0x00ff0000) >> 16;
+    g = (*pixel & 0x0000ff00) >> 8;
+    b = (*pixel & 0x000000ff);
+
+	if (a == 0)
+    {
+        r = 0;
+        g = 0;
+        b = 0;
+
+    }
+    else
+    {
+        /**
+         * Utilisation de la méthode employée dans la fonction
+         * _cairo_image_analyze_color() de cairo-image-surface.c.
+         *
+         * La documentation pour CAIRO_FORMAT_ARGB32 précise en effet :
+         *
+         *   """
+         *   each pixel is a 32-bit quantity, with alpha in the upper 8 bits,
+         *   then red, then green, then blue. The 32-bit quantities are
+         *   native-endian. Pre-multiplied alpha is used. (That is, 50%
+         *   transparent red is 0x80800000, not 0x80ff0000.)
+         *   """
+         */
+
+        r = (r * 255 + a / 2) / a;
+        g = (g * 255 + a / 2) / a;
+        b = (b * 255 + a / 2) / a;
+
+    }
+
+    color->alpha = (1.0 * a) / 0xff;
+    color->red = (1.0 * r) / 0xff;
+    color->green = (1.0 * g) / 0xff;
+    color->blue = (1.0 * b) / 0xff;
+
+    result = true;
+
+    /* Renvoi des conclusions */
+
+ bad_cairo:
+
+    cairo_destroy(cr);
+
+ empty_surface:
+
+    cairo_surface_destroy(surface);
+
+    g_object_unref(G_OBJECT(area));
+
+    return result;
+
+}
 
 
 /******************************************************************************
diff --git a/src/gtkext/easygtk.h b/src/gtkext/easygtk.h
index f8c2212..f181d43 100644
--- a/src/gtkext/easygtk.h
+++ b/src/gtkext/easygtk.h
@@ -25,6 +25,7 @@
 #define _GTKEXT_EASYGTK_H
 
 
+#include <stdbool.h>
 #include <gtk/gtk.h>
 
 
@@ -92,6 +93,10 @@ GtkWidget *qck_create_menu_separator(void);
 
 
 
+
+/* Identifie la couleur de base associée à un style GTK. */
+bool get_color_from_style(const char *, bool, GdkRGBA *);
+
 /* Détermine l'indice d'un composant dans un conteneur GTK. */
 gint find_contained_child_index(GtkContainer *, GtkWidget *);
 
-- 
cgit v0.11.2-87-g4458