/* Chrysalide - Outil d'analyse de fichiers binaires * theme.c - prototypes pour l'ajout d'extensions au thème GTK * * Copyright (C) 2016-2017 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 #include #include #include #include #include #include #include #include #include #include "../../common/extstr.h" #include "../../common/xdg.h" #include "../../core/logs.h" #include "../../core/params.h" /* Parcourt tous les répertoires connus pour trouver un thème. */ static char *browse_themes_directories(const char *, gboolean); /* Parcourt un répertoire donné à la recherche d'un thème. */ static char *look_for_named_theme(const char *, const char *, gboolean); /* Ajoute les définitions CSS à partir d'un chemin donné. */ static void load_css_partial_content(char **, const char *, gboolean); /* Etend le thème courant de GTK. */ static void activate_css_content(GdkScreen *, const char *); /* Répertoires de recherche */ static const char *_themes_directories[] = { PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "themes", THEMES_DIR, NULL }; /****************************************************************************** * * * 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 ? */ char *content; /* Définitions CSS à charger */ 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); content = browse_themes_directories(name, dark); if (content != NULL) { activate_css_content(screen, content); free(content); result = true; } legt_done: return result; } /****************************************************************************** * * * Paramètres : name = nom du thème recherché, et donc de son répertoire. * * dark = indique une préférence pour la variante foncée. * * * * Description : Parcourt tous les répertoires connus pour trouver un thème. * * * * Retour : Contenu CSS chargé ou NULL si aucun. * * * * Remarques : - * * * ******************************************************************************/ static char *browse_themes_directories(const char *name, gboolean dark) { char *result; /* Défintions CSS à renvoyer */ char *suffix; /* Fin du répertoire personnel */ char *owndir; /* Thèmes personnels ? */ const char **iter; /* Boucle de parcours */ result = NULL; /* Répertoire de l'utilisateur en premier ! */ suffix = strdup("chrysalide"); suffix = stradd(suffix, G_DIR_SEPARATOR_S); suffix = stradd(suffix, "themes"); owndir = get_xdg_config_dir(suffix); free(suffix); if (owndir != NULL) { result = look_for_named_theme(owndir, name, dark); free(owndir); } /* Parcours des autres répertoires classiques au besoin */ for (iter = _themes_directories; *iter != NULL && result == NULL; iter++) result = look_for_named_theme(*iter, name, dark); return result; } /****************************************************************************** * * * Paramètres : 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. * * * * Description : Parcourt un répertoire donné à la recherche d'un thème. * * * * Retour : Contenu CSS chargé ou NULL si aucun. * * * * Remarques : - * * * ******************************************************************************/ static char *look_for_named_theme(const char *dirname, const char *name, gboolean dark) { char *result; /* Défintions CSS à 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 */ char *filename; /* Chemin d'accès constitué */ result = NULL; ret = asprintf(&path, "%s" G_DIR_SEPARATOR_S "%s", dirname, name); if (ret == -1) goto lfnt_done; dirfd = open(path, O_RDONLY | O_DIRECTORY); if (dirfd == -1) goto lfnt_not_found; int keep_css_only(const struct dirent *entry) { return (entry->d_type == DT_REG && endswith(entry->d_name, ".css") && !endswith(entry->d_name, "-dark.css") ? 1 : 0); } count = scandirat(dirfd, ".", &namelist, keep_css_only, alphasort); for (i = 0; i < count; i++) { ret = asprintf(&filename, "%s%s%s", path, G_DIR_SEPARATOR_S, namelist[i]->d_name); if (ret == -1) continue; load_css_partial_content(&result, filename, dark); free(filename); } if (count > 0) free(namelist); close(dirfd); lfnt_not_found: free(path); lfnt_done: return result; } /****************************************************************************** * * * Paramètres : content = définitions CSS à compléter. [OUT] * * path = chemin vers la nouvelle définition à ajouter. * * dark = indique une préférence pour la variante foncée. * * * * Description : Ajoute les définitions CSS à partir d'un chemin donné. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void load_css_partial_content(char **content, const char *path, gboolean dark) { bool got_it; /* Version sombre présente ? */ char *dark_path; /* Version sombre du chemin */ GtkCssProvider *provider; /* Nouveau fournisseur CSS */ GError *error; /* Relevé d'éventuelles erreurs*/ char *extra; /* Contenu d'une feuille */ got_it = false; if (dark) { dark_path = strdup(path); dark_path = strrpl(dark_path, ".css", "-dark.css"); provider = gtk_css_provider_new(); error = NULL; gtk_css_provider_load_from_path(provider, dark_path, &error); if (error == NULL) { log_variadic_message(LMT_INFO, _("Loaded CSS definitions from '%s'"), dark_path); extra = gtk_css_provider_to_string(provider); *content = stradd(*content, extra); free(extra); got_it = true; } else g_error_free(error); g_object_unref(G_OBJECT(provider)); } if (!got_it) { provider = gtk_css_provider_new(); error = NULL; gtk_css_provider_load_from_path(provider, path, &error); if (error == NULL) { log_variadic_message(LMT_INFO, _("Loaded CSS definitions from '%s'"), path); extra = gtk_css_provider_to_string(provider); *content = stradd(*content, extra); free(extra); } else { log_variadic_message(LMT_ERROR, _("Failed to load CSS definitions from '%s'"), path); g_error_free(error); } g_object_unref(G_OBJECT(provider)); } } /****************************************************************************** * * * Paramètres : screen = écran concerné par les éventuels chargements. * * content = contenu CSS reconstitué à charger. * * * * Description : Etend le thème courant de GTK. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void activate_css_content(GdkScreen *screen, const char *content) { GtkCssProvider *provider; /* Nouveau fournisseur CSS */ GError *error; /* Relevé d'éventuelles erreurs*/ provider = gtk_css_provider_new(); error = NULL; gtk_css_provider_load_from_data(provider, content, -1, &error); if (error == NULL) { gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); } else { assert(false); log_variadic_message(LMT_ERROR, _("Failed to load the global CSS extra definition")); g_error_free(error); } g_object_unref(G_OBJECT(provider)); }