From 799edfed201e47b5d16fa811ffc77231695000d7 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 9 Apr 2019 08:14:53 +0200
Subject: Introduced a new theme format.

---
 .gitignore                    |   3 +
 src/gui/Makefile.am           |   3 +-
 src/gui/core/core.c           |  12 +-
 src/gui/core/theme.c          | 294 ++++++++++++++----------------
 src/gui/core/theme.h          |  11 +-
 src/gui/theme.c               | 403 ++++++++++++++++++++++++++++++++++++++++++
 src/gui/theme.h               |  63 +++++++
 themes/Adwaita/Makefile.am    |  12 +-
 themes/Adwaita/definition.xml |  22 +++
 themes/Adwaita/gresource.xml  |  12 ++
 10 files changed, 668 insertions(+), 167 deletions(-)
 create mode 100644 src/gui/theme.c
 create mode 100644 src/gui/theme.h
 create mode 100644 themes/Adwaita/definition.xml
 create mode 100644 themes/Adwaita/gresource.xml

diff --git a/.gitignore b/.gitignore
index a3fb693..d67d2d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,3 +73,6 @@ tools/d2c/d2c
 
 # Misc
 plugins/python/androperms/androperms.db
+
+# Themes
+*.ctm
diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am
index 879b42d..9aceec7 100644
--- a/src/gui/Makefile.am
+++ b/src/gui/Makefile.am
@@ -8,7 +8,8 @@ libgui_la_SOURCES =						\
 	editor.h editor.c					\
 	panel-int.h							\
 	panel.h panel.c						\
-	status.h status.c
+	status.h status.c					\
+	theme.h theme.c
 
 libgui_la_LIBADD =						\
 	core/libguicore.la					\
diff --git a/src/gui/core/core.c b/src/gui/core/core.c
index 8783783..a0904f8 100644
--- a/src/gui/core/core.c
+++ b/src/gui/core/core.c
@@ -90,10 +90,16 @@ bool load_all_gui_components(void)
 bool complete_loading_of_all_gui_components(GGenConfig *config)
 {
     bool result;                            /* Bilan à faire remonter      */
+    const char *name;                       /* Nom du thème recherché      */
     GtkTiledGrid *grid;                     /* Composant d'affichage       */
     GPanelItem *welcome;                    /* Panneau d'accueil           */
 
-    load_extra_gtk_theme();
+    result = g_generic_config_get_value(config, MPK_INTERNAL_THEME, &name);
+    if (!result) goto no_theme;
+
+    load_all_themes();
+
+    apply_gtk_theme(name);
 
     result = load_segment_rendering_parameters();
 
@@ -119,6 +125,8 @@ bool complete_loading_of_all_gui_components(GGenConfig *config)
 
     gtk_tiled_grid_restore_positions(grid, config);
 
+ no_theme:
+
     return result;
 
 }
@@ -140,4 +148,6 @@ void unload_all_gui_components(void)
 {
     exit_segment_content_hash_table();
 
+    unload_all_themes();
+
 }
diff --git a/src/gui/core/theme.c b/src/gui/core/theme.c
index 77eff7f..83bcf4e 100644
--- a/src/gui/core/theme.c
+++ b/src/gui/core/theme.c
@@ -39,22 +39,16 @@
 #include <i18n.h>
 
 
+#include "../theme.h"
 #include "../../common/extstr.h"
 #include "../../common/xdg.h"
 #include "../../core/logs.h"
-#include "../../core/params.h"
 #include "../../plugins/pglist.h"
 
 
 
-/* Parcourt tous les répertoires connus pour trouver un thème. */
-static bool browse_themes_directories(GdkScreen *, const char *, gboolean);
-
-/* Parcourt un répertoire donné à la recherche d'un thème. */
-static bool look_for_named_theme(GdkScreen *, const char *, const char *, gboolean);
-
-/* Ajoute les définitions CSS à partir d'un chemin donné. */
-static bool load_css_content(GdkScreen *, const char *, gboolean);
+/* Parcourt un répertoire donné à la recherche de thèmes. */
+static void look_for_editor_themes(const char *);
 
 /* Parcourt tous les greffons à la recherche de définitions CSS. */
 static bool extend_with_plugins_themes(GdkScreen *, gboolean);
@@ -72,56 +66,15 @@ static const char *_themes_directories[] = {
 
 };
 
+/* Liste de thèmes utilisables */
+static GEditorTheme **_themes = NULL;
+static size_t _theme_count = 0;
 
 
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : -                                                            *
-*                                                                             *
-*  Description : Charge le thème GTK pour les composants spécifiques.         *
-*                                                                             *
-*  Retour      : true ou false selon le bilan de l'opération.                 *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-bool load_extra_gtk_theme(void)
-{
-    bool result;                            /* Bilan à faire remonter      */
-    const char *name;                       /* Nom du thème recherché      */
-    GdkScreen *screen;                      /* Ecran(s) concerné(s)        */
-    GtkSettings *settings;                  /* Propriétés du système       */
-    gboolean dark;                          /* Envie d'un thème sombre ?   */
-
-    result = false;
-
-    if (!g_generic_config_get_value(get_main_configuration(), MPK_INTERNAL_THEME, &name))
-        goto legt_done;
-
-    screen = gdk_screen_get_default();
-
-    settings = gtk_settings_get_for_screen(screen);
-
-    g_object_get(settings, "gtk-application-prefer-dark-theme", &dark, NULL);
-
-    result = browse_themes_directories(screen, name, dark);
-
-    if (result)
-        result = extend_with_plugins_themes(screen, dark);
-
- legt_done:
-
-    return result;
-
-}
-
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : screen = écran visé par le chargement d'un thème.            *
-*                name   = nom du thème recherché, et donc de son répertoire.  *
-*                dark   = indique une préférence pour la variante foncée.     *
+*  Paramètres  : -                                                            *
 *                                                                             *
 *  Description : Parcourt tous les répertoires connus pour trouver un thème.  *
 *                                                                             *
@@ -131,15 +84,12 @@ bool load_extra_gtk_theme(void)
 *                                                                             *
 ******************************************************************************/
 
-static bool browse_themes_directories(GdkScreen *screen, const char *name, gboolean dark)
+void load_all_themes(void)
 {
-    bool result;                            /* Bilan à renvoyer            */
     char *suffix;                           /* Fin du répertoire personnel */
     char *owndir;                           /* Thèmes personnels ?         */
     const char **iter;                      /* Boucle de parcours          */
 
-    result = false;
-
     /* Répertoire de l'utilisateur en premier ! */
 
     suffix = strdup("chrysalide");
@@ -152,74 +102,70 @@ static bool browse_themes_directories(GdkScreen *screen, const char *name, gbool
 
     if (owndir != NULL)
     {
-        result = look_for_named_theme(screen, owndir, name, dark);
-
+        look_for_editor_themes(owndir);
         free(owndir);
-
     }
 
     /* Parcours des autres répertoires classiques au besoin */
 
-    for (iter = _themes_directories; *iter != NULL && !result; iter++)
-        result = look_for_named_theme(screen, *iter, name, dark);
-
-    return result;
+    for (iter = _themes_directories; *iter != NULL; iter++)
+        look_for_editor_themes(*iter);
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : screen  = écran visé par le chargement d'un thème.           *
-*                dirname = chemin du répertoire où effectuer des recherches.  *
-*                name    = nom du thème recherché, et donc de son répertoire. *
-*                dark    = indique une préférence pour la variante foncée.    *
+*  Paramètres  : dirname = chemin du répertoire où effectuer des recherches.  *
 *                                                                             *
-*  Description : Parcourt un répertoire donné à la recherche d'un thème.      *
+*  Description : Parcourt un répertoire donné à la recherche de thèmes.       *
 *                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
+*  Retour      : -                                                            *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool look_for_named_theme(GdkScreen *screen, const char *dirname, const char *name, gboolean dark)
+static void look_for_editor_themes(const char *dirname)
 {
-    bool result;                            /* Bilan à renvoyer            */
-    char *path;                             /* Chemin d'accès à essayer    */
-    int ret;                                /* Bilan d'un appel            */
     int dirfd;                              /* Canal de lecture            */
     struct dirent **namelist;               /* Liste des trouvailles       */
     int count;                              /* Nombre de fichiers trouvés  */
     int i;                                  /* Boucle de parcours          */
+    struct dirent *entry;                   /* Raccourci d'usage           */
+    int ret;                                /* Bilan d'un appel            */
     char *filename;                         /* Chemin d'accès constitué    */
+    GEditorTheme *theme;                    /* Nouveau thème valide        */
 
-    result = false;
-
-    ret = asprintf(&path, "%s" G_DIR_SEPARATOR_S "%s", dirname, name);
-    if (ret == -1) goto lfnt_done;
+    dirfd = open(dirname, O_RDONLY | O_DIRECTORY);
+    if (dirfd == -1) goto not_found;
 
-    dirfd = open(path, O_RDONLY | O_DIRECTORY);
-    if (dirfd == -1) goto lfnt_not_found;
+    count = scandirat(dirfd, ".", &namelist, NULL, alphasort);
 
-    int keep_css_only(const struct dirent *entry)
+    for (i = 0; i < count; i++)
     {
-        return (entry->d_type == DT_REG
-                && endswith(entry->d_name, ".css")
-                && !endswith(entry->d_name, "-dark.css") ? 1 : 0);
+        entry = namelist[i];
 
-    }
+        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
+            continue;
 
-    count = scandirat(dirfd, ".", &namelist, keep_css_only, alphasort);
+        ret = asprintf(&filename, "%s%s%s", dirname, G_DIR_SEPARATOR_S, entry->d_name);
+        if (ret == -1) continue;
 
-    result = (count > 0);
+        if (entry->d_type == DT_DIR)
+            look_for_editor_themes(filename);
 
-    for (i = 0; i < count && result; i++)
-    {
-        ret = asprintf(&filename, "%s%s%s", path, G_DIR_SEPARATOR_S, namelist[i]->d_name);
-        if (ret == -1) continue;
+        else if (entry->d_type == DT_REG)
+        {
+            theme = g_editor_theme_new(filename, true);
 
-        result = load_css_content(screen, filename, dark);
+            if (theme != NULL)
+            {
+                _themes = realloc(_themes, ++_theme_count * sizeof(GEditorTheme *));
+                _themes[_theme_count - 1] = theme;
+            }
+
+        }
 
         free(filename);
 
@@ -230,11 +176,80 @@ static bool look_for_named_theme(GdkScreen *screen, const char *dirname, const c
 
     close(dirfd);
 
- lfnt_not_found:
+ not_found:
 
-    free(path);
+    ;
 
- lfnt_done:
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Décharge tous les thèmes référencés en mémoire.              *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void unload_all_themes(void)
+{
+    size_t i;                               /* Boucle de parcours          */
+
+    for (i = 0; i < _theme_count; i++)
+        g_object_unref(G_OBJECT(_themes[i]));
+
+    if (_themes != NULL)
+        free(_themes);
+
+    _theme_count = 0;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : name = désignation du thème GTK à charger.                   *
+*                                                                             *
+*  Description : Charge le thème GTK pour les composants spécifiques.         *
+*                                                                             *
+*  Retour      : true ou false selon le bilan de l'opération.                 *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool apply_gtk_theme(const char *name)
+{
+    bool result;                            /* Bilan à faire remonter      */
+    size_t i;                               /* Boucle de parcours          */
+    GdkScreen *screen;                      /* Ecran(s) concerné(s)        */
+    GtkSettings *settings;                  /* Propriétés du système       */
+    gboolean dark;                          /* Envie d'un thème sombre ?   */
+
+    result = false;
+
+    for (i = 0; i < _theme_count; i++)
+        if (strcmp(name, g_editor_theme_get_name(_themes[i])) == 0)
+        {
+            result = true;
+            break;
+        }
+
+    if (result)
+    {
+        screen = gdk_screen_get_default();
+
+        settings = gtk_settings_get_for_screen(screen);
+
+        g_object_get(settings, "gtk-application-prefer-dark-theme", &dark, NULL);
+
+        g_editor_theme_load(_themes[i], screen, dark);
+
+    }
 
     return result;
 
@@ -255,16 +270,12 @@ static bool look_for_named_theme(GdkScreen *screen, const char *dirname, const c
 *                                                                             *
 ******************************************************************************/
 
-static bool load_css_content(GdkScreen *screen, const char *path, gboolean dark)
+GtkCssProvider *load_css_content(GdkScreen *screen, const char *path)
 {
-    bool result;                            /* Bilan à renvoyer            */
-    GtkCssProvider *provider;               /* Nouveau fournisseur CSS     */
-    char *dark_path;                        /* Version sombre du chemin    */
+    GtkCssProvider *result;                 /* Fournisseur à renvoyer      */
     GFile *file;                            /* Fichier à charger           */
     GError *error;                          /* Relevé d'éventuelles erreurs*/
 
-    result = false;
-
     /**
      * Comme GTK exporte les images sous forme de données encodées en base 64
      * (cf. gtk_css_image_surface_print()) et ne sait pas recharger ensuite
@@ -278,75 +289,32 @@ static bool load_css_content(GdkScreen *screen, const char *path, gboolean dark)
      * Ce constat est valable pour la version 3.22.11-1 de Debian, au moins.
      */
 
-    if (dark)
-    {
-        provider = gtk_css_provider_new();
-
-        error = NULL;
+    result = gtk_css_provider_new();
 
-        dark_path = strdup(path);
-        dark_path = strrpl(dark_path, ".css", "-dark.css");
+    error = NULL;
 
-        if (strstr(path, "://") != NULL)
-            file = g_file_new_for_uri(dark_path);
-        else
-            file = g_file_new_for_path(dark_path);
+    assert(strstr(path, "://") != NULL);
 
-        gtk_css_provider_load_from_file(provider, file, &error);
+    file = g_file_new_for_uri(path);
 
-        g_object_unref(G_OBJECT(file));
+    gtk_css_provider_load_from_file(result, file, &error);
 
-        if (error == NULL)
-        {
-            log_variadic_message(LMT_INFO, _("Loaded CSS definitions from '%s'"), dark_path);
+    g_object_unref(G_OBJECT(file));
 
-            gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER(provider),
-                                                      GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-
-            result = true;
-
-        }
-        else
-            g_error_free(error);
-
-        free(dark_path);
+    if (error == NULL)
+    {
+        log_variadic_message(LMT_INFO, _("Loaded CSS definitions from '%s'"), path);
 
-        g_object_unref(G_OBJECT(provider));
+        gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER(result),
+                                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
 
     }
-
-    if (!result)
+    else
     {
-        provider = gtk_css_provider_new();
-
-        error = NULL;
-
-        if (strstr(path, "://") != NULL)
-            file = g_file_new_for_uri(path);
-        else
-            file = g_file_new_for_path(path);
-
-        gtk_css_provider_load_from_file(provider, file, &error);
-
-        g_object_unref(G_OBJECT(file));
+        log_variadic_message(LMT_ERROR, _("Failed to load CSS definitions from '%s'"), path);
+        g_error_free(error);
 
-        if (error == NULL)
-        {
-            log_variadic_message(LMT_INFO, _("Loaded CSS definitions from '%s'"), path);
-
-            gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER(provider),
-                                                      GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-
-            result = true;
-
-        }
-        else
-        {
-            log_variadic_message(LMT_ERROR, _("Failed to load CSS definitions from '%s'"), path);
-            g_error_free(error);
-        }
-
-        g_object_unref(G_OBJECT(provider));
+        g_object_unref(G_OBJECT(result));
 
     }
 
@@ -374,6 +342,7 @@ static bool extend_with_plugins_themes(GdkScreen *screen, gboolean dark)
     char **resources;                       /* Fichiers supplémentaires    */
     size_t count;                           /* Nombre de ces fichiers      */
     size_t i;                               /* Boucle de parcours          */
+    GtkCssProvider *provider;               /* Nouveau fournisseur CSS     */
 
     result = true;
 
@@ -384,8 +353,11 @@ static bool extend_with_plugins_themes(GdkScreen *screen, gboolean dark)
 
     for (i = 0; i < count && result; i++)
     {
-        result = load_css_content(screen, resources[i], dark);
+        provider = load_css_content(screen, resources[i]);
+        g_clear_object(&provider);
+
         free(resources[i]);
+
     }
 
     if (resources != NULL)
diff --git a/src/gui/core/theme.h b/src/gui/core/theme.h
index dfa7d34..52c6fb0 100644
--- a/src/gui/core/theme.h
+++ b/src/gui/core/theme.h
@@ -31,8 +31,17 @@
 
 
 
+/* Parcourt tous les répertoires connus pour trouver un thème. */
+void load_all_themes(void);
+
+/* Décharge tous les thèmes référencés en mémoire. */
+void unload_all_themes(void);
+
 /* Charge le thème GTK pour les composants spécifiques. */
-bool load_extra_gtk_theme(void);
+bool apply_gtk_theme(const char *);
+
+/* Ajoute les définitions CSS à partir d'un chemin donné. */
+GtkCssProvider *load_css_content(GdkScreen *, const char *);
 
 
 
diff --git a/src/gui/theme.c b/src/gui/theme.c
new file mode 100644
index 0000000..dc3d1bb
--- /dev/null
+++ b/src/gui/theme.c
@@ -0,0 +1,403 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * theme.c - gestion d'un thème pour l'interface grahique
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide 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.
+ *
+ *  Chrysalide 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "theme.h"
+
+
+#include <malloc.h>
+#include <stdio.h>
+#include <string.h>
+#include <gio/gio.h>
+
+
+#include "core/theme.h"
+#include "../common/xml.h"
+
+
+
+/* Thème graphique pour l'éditeur (instance) */
+struct _GEditorTheme
+{
+    GObject parent;                         /* A laisser en premier        */
+
+    GResource *resource;                    /* Resources GLib associées    */
+
+    char *name;                             /* Désignation courte du thème */
+
+    xmlDocPtr xdoc;                         /* Document XML récupéré       */
+    xmlXPathContextPtr context;             /* Contexte d'analyse associé  */
+
+    GtkCssProvider **providers;             /* Fournisseur CSS appliqués   */
+    size_t count;                           /* Quantité de ces fournisseurs*/
+
+};
+
+
+/* Thème graphique pour l'éditeur (classe) */
+struct _GEditorThemeClass
+{
+    GObjectClass parent;                    /* A laisser en premier        */
+
+};
+
+
+/* Initialise la classe des éléments réactifs de l'éditeur. */
+static void g_editor_theme_class_init(GEditorThemeClass *);
+
+/* Initialise une instance d'élément réactif pour l'éditeur. */
+static void g_editor_theme_init(GEditorTheme *);
+
+/* Supprime toutes les références externes. */
+static void g_editor_theme_dispose(GEditorTheme *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_editor_theme_finalize(GEditorTheme *);
+
+/* Active une partie choisie de thème graphique. */
+static void g_editor_theme_load_section(GEditorTheme *, GdkScreen *, gboolean, const char *);
+
+
+
+/* Indique le type défini pour un theme de l'interface graphique. */
+G_DEFINE_TYPE(GEditorTheme, g_editor_theme, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : klass = classe à initialiser.                                *
+*                                                                             *
+*  Description : Initialise la classe des éléments réactifs de l'éditeur.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_editor_theme_class_init(GEditorThemeClass *klass)
+{
+    GObjectClass *object;                   /* Autre version de la classe  */
+
+    object = G_OBJECT_CLASS(klass);
+
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_editor_theme_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_editor_theme_finalize;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : theme = instance à initialiser.                              *
+*                                                                             *
+*  Description : Initialise une instance d'élément réactif pour l'éditeur.    *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_editor_theme_init(GEditorTheme *theme)
+{
+    theme->resource = NULL;
+
+    theme->name = NULL;
+
+    theme->xdoc = NULL;
+    theme->context = NULL;
+
+    theme->providers = NULL;
+    theme->count = 0;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : theme = instance d'objet GLib à traiter.                     *
+*                                                                             *
+*  Description : Supprime toutes les références externes.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_editor_theme_dispose(GEditorTheme *theme)
+{
+    if (theme->resource != NULL)
+    {
+        g_resource_unref(theme->resource);
+        theme->resource = NULL;
+    }
+
+    G_OBJECT_CLASS(g_editor_theme_parent_class)->dispose(G_OBJECT(theme));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : theme = instance d'objet GLib à traiter.                     *
+*                                                                             *
+*  Description : Procède à la libération totale de la mémoire.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_editor_theme_finalize(GEditorTheme *theme)
+{
+    if (theme->name != NULL)
+        free(theme->name);
+
+    if (theme->xdoc != NULL)
+        close_xml_file(theme->xdoc, theme->context);
+
+    G_OBJECT_CLASS(g_editor_theme_parent_class)->finalize(G_OBJECT(theme));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : filename = chemin d'accès vers un ressource.                 *
+*                include  = enregistre la ressource dans l'espace global ?    *
+*                                                                             *
+*  Description : Charge les éléments associés à un thème graphique.           *
+*                                                                             *
+*  Retour      : Adresse de la représentation ou NULL en cas d'échec.         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GEditorTheme *g_editor_theme_new(const char *filename, bool include)
+{
+    GEditorTheme *result;                   /* Adresse à retourner         */
+    GResource *resource;                    /* Resources du thème          */
+    char **children;                        /* Sous-éléments présents      */
+    size_t name_len;                        /* Taille de la désignation    */
+    char *name;                             /* Désignation courte du thème */
+    char *path;                             /* Chemin vers à la définition */
+    GBytes *bytes;                          /* Données d'identité          */
+    const char *data;                       /* Données XML de définition   */
+    size_t data_len;                        /* Quantité de ces données     */
+    xmlDocPtr xdoc;                         /* Document XML récupéré       */
+    xmlXPathContextPtr context;             /* Contexte d'analyse associé  */
+    bool status;                            /* Bilan de chargement         */
+
+    /* Ouverture de la ressource */
+
+    resource = g_resource_load(filename, NULL);
+    if (resource == NULL) goto bad_res;
+
+    children = g_resource_enumerate_children(resource, "/org/chrysalide/gui/themes",
+                                             G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+
+    if (children == NULL || children[0] == NULL)
+        goto empty_res;
+
+    name_len = strlen(children[0]);
+
+    if (name_len < 2 || children[0][name_len - 1] != '/')
+        goto empty_res;
+
+    name = strndup(children[0], name_len - 1);
+
+    /* Chargement de la définition XML */
+
+    asprintf(&path, "/org/chrysalide/gui/themes/%s/definition.xml", name);
+
+    bytes = g_resource_lookup_data(resource, path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+
+    free(path);
+
+    if (bytes == NULL)
+        goto no_def;
+
+    data = g_bytes_get_data(bytes, &data_len);
+
+    status = load_xml_from_memory(data, data_len, &xdoc, &context);
+
+    g_bytes_unref(bytes);
+
+    if (!status)
+        goto bad_xml;
+
+    /* Création du thème */
+
+    result = g_object_new(G_TYPE_EDITOR_THEME, NULL);
+
+    result->resource = resource;
+
+    result->name = name;
+
+    result->xdoc = xdoc;
+    result->context = context;
+
+    if (include)
+        g_resources_register(result->resource);
+
+    return result;
+
+ bad_xml:
+
+ no_def:
+
+    free(name);
+
+ empty_res:
+
+    g_resource_unref(resource);
+
+ bad_res:
+
+    return NULL;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : theme = theme graphique à consulter.                         *
+*                                                                             *
+*  Description : Indique le nom d'un thème donné.                             *
+*                                                                             *
+*  Retour      : Désignation courte associée au thème.                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+const char *g_editor_theme_get_name(const GEditorTheme *theme)
+{
+    const char *result;                     /* Désignation à retourner     */
+
+    result = theme->name;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : theme   = theme graphique à charger.                         *
+*                screen  = écran visé par le chargement d'un thème.           *
+*                dark    = indique une préférence pour la variante foncée.    *
+*                section = sous-partie de la définition à traiter.            *
+*                                                                             *
+*  Description : Active une partie choisie de thème graphique.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_editor_theme_load_section(GEditorTheme *theme, GdkScreen *screen, gboolean dark, const char *section)
+{
+    char *def_prefix;                       /* Base de ressource par défaut*/
+    char *sec_access;                       /* Chemin d'accès à la section */
+    xmlXPathObjectPtr xobject;              /* Cible d'une recherche       */
+    unsigned int i;                         /* Boucle de parcours          */
+    char *access;                           /* Chemin d'accès à un élément */
+    char *value;                            /* Resource à charger          */
+    char *path;                             /* Chemin d'accès final        */
+    GtkCssProvider *provider;               /* Nouveau fournisseur CSS     */
+
+    asprintf(&def_prefix, "/org/chrysalide/gui/themes/%s/", theme->name);
+
+    asprintf(&sec_access, "/ChrysalideTheme/resources/%s/path", section);
+
+    xobject = get_node_xpath_object(theme->context, sec_access);
+
+    for (i = 0; i < XPATH_OBJ_NODES_COUNT(xobject); i++)
+    {
+        asprintf(&access, "%s[position()=%u]", sec_access, i + 1);
+
+        value = get_node_text_value(theme->context, access);
+
+        free(access);
+
+        if (strlen(value) > 0)
+        {
+            if (value[0] == '/')
+                asprintf(&path, "resource://%s", value);
+            else
+                asprintf(&path, "resource://%s%s", def_prefix, value);
+
+            provider = load_css_content(screen, path);
+
+            if (provider != NULL)
+            {
+                theme->providers = realloc(theme->providers, ++theme->count * sizeof(GtkCssProvider *));
+                theme->providers[theme->count - 1] = provider;
+            }
+
+        }
+
+        free(value);
+
+    }
+
+    if(xobject != NULL)
+        xmlXPathFreeObject(xobject);
+
+    free(sec_access);
+
+    free(def_prefix);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : theme  = theme graphique à charger.                          *
+*                screen = écran visé par le chargement d'un thème.            *
+*                dark   = indique une préférence pour la variante foncée.     *
+*                                                                             *
+*  Description : Active un thème graphique particulier.                       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_editor_theme_load(GEditorTheme *theme, GdkScreen *screen, gboolean dark)
+{
+    g_editor_theme_load_section(theme, screen, dark, "common");
+
+    if (dark)
+        g_editor_theme_load_section(theme, screen, dark, "dark");
+    else
+        g_editor_theme_load_section(theme, screen, dark, "light");
+
+}
diff --git a/src/gui/theme.h b/src/gui/theme.h
new file mode 100644
index 0000000..000347b
--- /dev/null
+++ b/src/gui/theme.h
@@ -0,0 +1,63 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * theme.h - prototypes pour la gestion d'un thème pour l'interface grahique
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide 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.
+ *
+ *  Chrysalide 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _GUI_THEME_H
+#define _GUI_THEME_H
+
+
+#include <stdbool.h>
+#include <gtk/gtk.h>
+
+
+
+#define G_TYPE_EDITOR_THEME            g_editor_theme_get_type()
+#define G_EDITOR_THEME(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_EDITOR_THEME, GEditorTheme))
+#define G_IS_EDITOR_THEME(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_EDITOR_THEME))
+#define G_EDITOR_THEME_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_EDITOR_THEME, GEditorThemeClass))
+#define G_IS_EDITOR_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_EDITOR_THEME))
+#define G_EDITOR_THEME_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_EDITOR_THEME, GEditorThemeClass))
+
+
+/* Thème graphique pour l'éditeur (instance) */
+typedef struct _GEditorTheme GEditorTheme;
+
+/* Thème graphique pour l'éditeur (classe) */
+typedef struct _GEditorThemeClass GEditorThemeClass;
+
+
+/* Indique le type défini pour un theme de l'interface graphique. */
+GType g_editor_theme_get_type(void);
+
+/* Charge les éléments associés à un thème graphique. */
+GEditorTheme *g_editor_theme_new(const char *, bool);
+
+/* Indique le nom d'un thème donné. */
+const char *g_editor_theme_get_name(const GEditorTheme *);
+
+/* Active un thème graphique particulier. */
+void g_editor_theme_load(GEditorTheme *, GdkScreen *, gboolean);
+
+
+
+#endif  /* _GUI_THEME_H */
diff --git a/themes/Adwaita/Makefile.am b/themes/Adwaita/Makefile.am
index cf4d6a3..dd9b215 100644
--- a/themes/Adwaita/Makefile.am
+++ b/themes/Adwaita/Makefile.am
@@ -1,6 +1,7 @@
 
 GTK3_CSS =								\
 	core.css							\
+	definition.xml						\
 	display.css							\
 	icons.css							\
 	portions.css						\
@@ -10,8 +11,13 @@ GTK3_CSS =								\
 	redo.png							\
 	undo.png
 
-css_DATA = $(GTK3_CSS)
+adwaita.ctm: gresource.xml $(GTK3_CSS)
+	glib-compile-resources --target=$@ --sourcedir=$(srcdir) gresource.xml
 
-cssdir = $(themesdir)/Adwaita
+css_DATA = adwaita.ctm
 
-EXTRA_DIST = $(css_DATA)
+cssdir = $(themesdir)
+
+EXTRA_DIST = $(css_DATA) gresource.xml
+
+CLEANFILES = $(css_DATA)
diff --git a/themes/Adwaita/definition.xml b/themes/Adwaita/definition.xml
new file mode 100644
index 0000000..b469695
--- /dev/null
+++ b/themes/Adwaita/definition.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ChrysalideTheme>
+  <identity>
+    <name>Adwaita</name>
+    <author>Cyrille Bagard</author>
+    <desc>Default theme for Chrysalide</desc>
+  </identity>
+  <resources>
+    <common>
+      <path>core.css</path>
+      <path>display.css</path>
+      <path>icons.css</path>
+      <path>portions.css</path>
+    </common>
+    <light>
+      <path>widgets.css</path>
+    </light>
+    <dark>
+      <path>widgets-dark.css</path>
+    </dark>
+  </resources>
+</ChrysalideTheme>
diff --git a/themes/Adwaita/gresource.xml b/themes/Adwaita/gresource.xml
new file mode 100644
index 0000000..32a8a09
--- /dev/null
+++ b/themes/Adwaita/gresource.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+    <gresource prefix="/org/chrysalide/gui/themes/Adwaita">
+        <file compressed="true">definition.xml</file>
+        <file compressed="true">core.css</file>
+        <file compressed="true">display.css</file>
+        <file compressed="true">icons.css</file>
+        <file compressed="true">portions.css</file>
+        <file compressed="true">widgets.css</file>
+        <file compressed="true">widgets-dark.css</file>
+    </gresource>
+</gresources>
-- 
cgit v0.11.2-87-g4458