summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2019-04-09 06:14:53 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2019-04-09 06:14:53 (GMT)
commit799edfed201e47b5d16fa811ffc77231695000d7 (patch)
tree5f539bb5498a594db48299fac059f72bcd92df9c
parent010c4d4c07d19ff3f50d0f3caa9fc519ec17df14 (diff)
Introduced a new theme format.
-rw-r--r--.gitignore3
-rw-r--r--src/gui/Makefile.am3
-rw-r--r--src/gui/core/core.c12
-rw-r--r--src/gui/core/theme.c294
-rw-r--r--src/gui/core/theme.h11
-rw-r--r--src/gui/theme.c403
-rw-r--r--src/gui/theme.h63
-rw-r--r--themes/Adwaita/Makefile.am12
-rw-r--r--themes/Adwaita/definition.xml22
-rw-r--r--themes/Adwaita/gresource.xml12
10 files changed, 668 insertions, 167 deletions
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>