[goffice] Allow themes defined as xml files. [#531070]
- From: Jean Bréfort <jbrefort src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [goffice] Allow themes defined as xml files. [#531070]
- Date: Thu, 12 Aug 2010 09:12:22 +0000 (UTC)
commit 709fb74e47298672e76e61239838342c1c12ff60
Author: Jean Brefort <jean brefort normalesup org>
Date: Thu Aug 12 11:13:11 2010 +0200
Allow themes defined as xml files. [#531070]
ChangeLog | 19 ++
NEWS | 1 +
docs/reference/goffice-0.8-sections.txt | 17 +-
goffice/graph/gog-graph-prefs.ui | 20 +-
goffice/graph/gog-graph.c | 37 ++--
goffice/graph/gog-plot-engine.c | 53 ----
goffice/graph/gog-styled-object.c | 2 +-
goffice/graph/gog-theme.c | 434 ++++++++++++++++++++++++++++---
goffice/graph/gog-theme.h | 5 +-
9 files changed, 463 insertions(+), 125 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index d6c4c12..e4806b1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,24 @@
2010-08-12 Jean Brefort <jean brefort normalesup org>
+ * docs/reference/goffice-0.8-sections.txt: update.
+ * goffice/graph/gog-graph-prefs.ui: allow for themes defined in xml files.
+ [#531070]
+ * goffice/graph/gog-graph.c (cb_theme_changed),
+ (gog_graph_populate_editor): ditto.
+ * goffice/graph/gog-plot-engine.c (_gog_plugin_services_init): ditto.
+ * goffice/graph/gog-styled-object.c (gog_styled_object_init_style): ditto.
+ * goffice/graph/gog-theme.c (gog_theme_finalize),
+ (gog_theme_find_element), (gog_theme_fillin_style),
+ (gog_theme_add_element), (gog_theme_get_local_name),
+ (gog_theme_get_description), (map_area_series_solid_default),
+ (map_area_series_solid_guppi), (map_area_series_solid_palette),
+ (build_predefined_themes), (name_start), (name_end), (desc_end),
+ (elem_start), (theme_load_from_uri), (themes_load_from_dir),
+ (_gog_themes_init): ditto.
+ * goffice/graph/gog-theme.h: ditto.
+
+2010-08-12 Jean Brefort <jean brefort normalesup org>
+
* goffice/graph/gog-error-bar.c (gog_error_bar_prefs): fix memory leak.
* plugins/plot_distrib/gog-boxplot.c (gog_box_plot_pref),
(gog_box_plot_populate_editor): fix memory leak and criticals. [#626665]
diff --git a/NEWS b/NEWS
index adef122..ae2791c 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ Jean:
* Don't crash when loading a corrupted chart. [#626206, #626263, #626305]
* Fix blur in zoomed charts.
* Fix memory leak and criticals in graph editor. [#626665]
+ * Allow themes defined as xml files. [#531070]
--------------------------------------------------------------------------
goffice 0.8.8:
diff --git a/docs/reference/goffice-0.8-sections.txt b/docs/reference/goffice-0.8-sections.txt
index 13dc5e3..fa49341 100644
--- a/docs/reference/goffice-0.8-sections.txt
+++ b/docs/reference/goffice-0.8-sections.txt
@@ -835,11 +835,10 @@ GOG_THEME
GOG_IS_THEME
GogTheme
gog_theme_get_type
-gog_theme_new
-gog_theme_new_from_file
gog_theme_get_name
+gog_theme_get_local_name
+gog_theme_get_description
gog_theme_fillin_style
-gog_theme_registry_add
gog_theme_registry_lookup
gog_theme_registry_get_theme_names
</SECTION>
@@ -3026,6 +3025,18 @@ goc_line_get_type
</SECTION>
<SECTION>
+<FILE>goc-path</FILE>
+<TITLE>GocPath</TITLE>
+GocPath
+GocPathClass
+<SUBSECTION Standard>
+GOC_PATH
+GOC_IS_PATH
+GOC_TYPE_PATH
+goc_path_get_type
+</SECTION>
+
+<SECTION>
<FILE>goc-pixbuf</FILE>
<TITLE>GocPixbuf</TITLE>
GocPixbuf
diff --git a/goffice/graph/gog-graph-prefs.ui b/goffice/graph/gog-graph-prefs.ui
index 5e24866..bdfddbb 100644
--- a/goffice/graph/gog-graph-prefs.ui
+++ b/goffice/graph/gog-graph-prefs.ui
@@ -1,17 +1,7 @@
<?xml version="1.0"?>
<interface>
+ <!-- interface-requires gtk+ 2.12 -->
<!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkListStore" id="model1">
- <columns>
- <!-- column-name gchararray -->
- <column type="gchararray"/>
- </columns>
- <data>
- <row>
- <col id="0" translatable="yes">Default</col>
- </row>
- </data>
- </object>
<object class="GtkTable" id="gog_graph_prefs">
<property name="visible">True</property>
<property name="border_width">12</property>
@@ -99,4 +89,12 @@
</packing>
</child>
</object>
+ <object class="GtkListStore" id="model1">
+ <columns>
+ <!-- column-name gchararray -->
+ <column type="gchararray"/>
+ <!-- column-name theme -->
+ <column type="GObject"/>
+ </columns>
+ </object>
</interface>
diff --git a/goffice/graph/gog-graph.c b/goffice/graph/gog-graph.c
index bdaa876..072af7d 100644
--- a/goffice/graph/gog-graph.c
+++ b/goffice/graph/gog-graph.c
@@ -166,20 +166,17 @@ gog_graph_type_name (GogObject const *gobj)
static void
cb_theme_changed (GtkComboBox *combo, GogGraph *graph)
{
- GSList *theme_names;
- char const *name;
- int index = gtk_combo_box_get_active (combo);
-
- theme_names = gog_theme_registry_get_theme_names ();
- if (theme_names == NULL)
- return;
-
- name = g_slist_nth_data (theme_names, index);
- g_slist_free (theme_names);
- if (name == NULL)
- return;
-
- gog_graph_set_theme (graph, gog_theme_registry_lookup (name));
+ GtkTreeIter iter;
+
+ if (gtk_combo_box_get_active_iter (combo, &iter)) {
+ GtkTreeModel *model = GTK_TREE_MODEL (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)));
+ GogTheme *theme = NULL;
+ gtk_tree_model_get (model, &iter, 1, &theme, -1);
+ if (theme != NULL) {
+ gog_graph_set_theme (graph, theme);
+ g_object_unref (theme);
+ }
+ }
}
static void
@@ -211,16 +208,24 @@ gog_graph_populate_editor (GogObject *gobj,
GtkWidget *box;
GtkWidget *combo;
GSList *ptr;
+ GtkListStore *model;
+ GtkTreeIter iter;
+ GogTheme *theme;
char const *graph_theme_name;
int count, index = 0;
graph_theme_name = gog_theme_get_name (graph->theme);
combo = go_gtk_builder_get_widget (gui, "theme_combo");
- gtk_list_store_clear (GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo))));
+ model = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)));
count = 0;
for (ptr = theme_names; ptr != NULL; ptr = ptr->next) {
- gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _(ptr->data));
+ theme = gog_theme_registry_lookup (ptr->data);
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter,
+ 0, gog_theme_get_local_name (theme),
+ 1, theme,
+ -1);
if (strcmp (ptr->data, graph_theme_name) == 0)
index = count;
count++;
diff --git a/goffice/graph/gog-plot-engine.c b/goffice/graph/gog-plot-engine.c
index afab7e9..adf20fa 100644
--- a/goffice/graph/gog-plot-engine.c
+++ b/goffice/graph/gog-plot-engine.c
@@ -354,58 +354,6 @@ GSF_CLASS (GogPlotTypeService, gog_plot_type_service,
GO_TYPE_PLUGIN_SERVICE_SIMPLE)
/***************************************************************************/
-/* Use a plugin service to define themes */
-
-#define GOG_TYPE_THEME_SERVICE (gog_theme_service_get_type ())
-#define GOG_THEME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_TYPE_THEME_SERVICE, GogThemeService))
-#define GOG_IS_THEME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_TYPE_THEME_SERVICE))
-
-GType gog_theme_service_get_type (void);
-
-typedef GOPluginServiceSimple GogThemeService;
-typedef GOPluginServiceSimpleClass GogThemeServiceClass;
-
-static void
-gog_theme_service_read_xml (GOPluginService *service, xmlNode *tree, GOErrorInfo **ret_error)
-{
- GogTheme *theme;
- xmlNode *ptr;
- char *path;
-
- for (ptr = tree->xmlChildrenNode; ptr != NULL; ptr = ptr->next)
- if (0 == xmlStrcmp (ptr->name, "file") &&
- NULL != (path = xmlNodeGetContent (ptr))) {
- if (!g_path_is_absolute (path)) {
- char const *dir = go_plugin_get_dir_name (
- go_plugin_service_get_plugin (service));
- char *tmp = g_build_filename (dir, path, NULL);
- g_free (path);
- path = tmp;
- }
- theme = gog_theme_new_from_file (go_plugin_service_get_description (service),
- path);
- gog_theme_registry_add (theme, FALSE);
- }
-}
-
-static char *
-gog_theme_service_get_description (GOPluginService *service)
-{
- return g_strdup (_("Chart Theme"));
-}
-
-static void
-gog_theme_service_class_init (GOPluginServiceClass *ps_class)
-{
- ps_class->read_xml = gog_theme_service_read_xml;
- ps_class->get_description = gog_theme_service_get_description;
-}
-
-GSF_CLASS (GogThemeService, gog_theme_service,
- gog_theme_service_class_init, NULL,
- GO_TYPE_PLUGIN_SERVICE_SIMPLE)
-
-/***************************************************************************/
/* Support regression curves engines in plugins */
#define GOG_TYPE_TREND_LINE_ENGINE_SERVICE (gog_trend_line_engine_service_get_type ())
@@ -665,7 +613,6 @@ _gog_plugin_services_init (void)
{
go_plugin_service_define ("plot_engine", &gog_plot_engine_service_get_type);
go_plugin_service_define ("plot_type", &gog_plot_type_service_get_type);
- go_plugin_service_define ("chart_theme", &gog_theme_service_get_type);
go_plugin_service_define ("trendline_engine", &gog_trend_line_engine_service_get_type);
go_plugin_service_define ("trendline_type", &gog_trend_line_service_get_type);
}
diff --git a/goffice/graph/gog-styled-object.c b/goffice/graph/gog-styled-object.c
index 624a1fb..b32f5fc 100644
--- a/goffice/graph/gog-styled-object.c
+++ b/goffice/graph/gog-styled-object.c
@@ -168,7 +168,7 @@ gog_styled_object_init_style (GogStyledObject *gso, GOStyle *style)
{
style->interesting_fields = GO_STYLE_OUTLINE | GO_STYLE_FILL; /* default */
gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
- style, GOG_OBJECT (gso), 0, FALSE);
+ style, GOG_OBJECT (gso), 0, style->interesting_fields);
}
static void
diff --git a/goffice/graph/gog-theme.c b/goffice/graph/gog-theme.c
index 426addf..037b658 100644
--- a/goffice/graph/gog-theme.c
+++ b/goffice/graph/gog-theme.c
@@ -3,6 +3,7 @@
* gog-theme.c :
*
* Copyright (C) 2003-2004 Jody Goldberg (jody gnome org)
+ * Copyright (C) 2010 Jean Brefort (jean brefort normalesup org)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
@@ -20,18 +21,149 @@
*/
#include <goffice/goffice-config.h>
+#include <goffice/goffice-priv.h>
#include <goffice/graph/gog-theme.h>
#include <goffice/graph/gog-object.h>
#include <goffice/utils/go-color.h>
#include <goffice/utils/go-gradient.h>
#include <goffice/utils/go-units.h>
#include <goffice/utils/go-marker.h>
+#include <gsf/gsf-input-gio.h>
#include <gsf/gsf-impl-utils.h>
#include <glib/gi18n-lib.h>
#include <string.h>
-typedef void (*GogThemeStyleMap) (GOStyle *style, unsigned ind);
+/**
+ * SECTION:gog-theme
+ * @short_description: a list of default styles to apply to appropriate graph elements.
+ *
+ * The library provides two hard coded themes, "Default", and "Guppi". Other themes
+ * are described in files, some of which might be distributed with the library.
+ *
+ * A file defining a theme is an xml file with a <GogTheme> root node. The contents
+ * must be: _name|name+, _description?|description*, GOStyle+.
+ *
+ * _name and name nodes:
+ *
+ * The _name node should be used for themes distributed with goffice, localized
+ * names will be in *.po files and only the default name for "C" locale needs to
+ * be there. Other files need at least one name node for the default name, and
+ * might have some translated names with an appropriate "xml:lang" attribute.
+ *
+ * _description and description nodes:
+ *
+ * These work just like name nodes. The difference is that no description node is
+ * mandatory. A theme can work perfectly without a description.
+ *
+ * GOStyle nodes:
+ *
+ * These nodes actually define the theme. Attributes and contents are:
+ * attributes: class, role.
+ * contents: (line|outline)?, fill?, marker?, font?, text_layout?
+ *
+ * The attributes define the target for the style. You might have a class and
+ * a role attribute, or just one of them. If the role attribute is given, the class attribute,
+ * if given, represents the class of the parent object.
+ * A GOStyle node with no class or role will be used
+ * as default when another style is missing. If several such nodes exist, the
+ * last one will be used. If no default style exists, the "Default" theme is applied for
+ * missing nodes.
+ * The list of GOStyle nodes might be:
+ * <table style="border-spacing:1cm 2mm">
+ * <thead><tr><td>class</td><td>role</td><td>contents</td><td>comments</td></tr></thead>
+ * <tr><td>GogGraph</td><td></td><td>outline, fill</td></tr>
+ * <tr><td>GogGraph</td><td>Title</td><td>outline, fill, font, text_layout</td><td>The graph title</td></tr>
+ * <tr><td>GogChart</td><td></td><td>outline, fill</td></tr>
+ * <tr><td>GogChart</td><td>Title</td><td>outline, fill, font, text_layout</td><td>The chart title</td></tr>
+ * <tr><td>GogLegend</td><td></td><td>outline, fill, font</td></tr>
+ * <tr><td>GogAxis</td><td></td><td>line, font, text_layout</td></tr>
+ * <tr><td>GogAxisLine</td><td></td><td>line, font</td></tr>
+ * <tr><td>GogGrid</td><td></td><td>outline, fill</td><td>GogGrid is actually the back plane</td></tr>
+ * <tr><td> </td><td>MajorGrid</td><td>line, fill</td></tr>
+ * <tr><td> </td><td>MinorGrid</td><td>line, fill</td></tr>
+ * <tr><td>GogLabel</td><td></td><td>outline, fill, font, text_layout</td></tr>
+ * <tr><td>GogSeries</td><td></td><td>line, fill, marker</td><td>One is needed for each entry in the palette</td></tr>
+ * <tr><td>GogTrendLine</td><td></td><td>line, fill</td></tr>
+ * <tr><td>GogReqEqn</td><td></td><td>line, fill, font, text_layout</td></tr>
+ * </table>
+ *
+ * The line and outline nodes are actually the same so using line in place of outline is
+ * not an issue. A color is specified either using the format RR:GG::BB:AA or a string as defined
+ * in rgb.txt, so black can be specified as "black" or "00:00:00:FF".
+ *
+ * "line" or "outline" node:
+ *
+ * <table style="border-spacing:1cm 2mm">
+ * <thead><tr><td>attribute</td><td>value</td></tr><td>comments</td></thead>
+ * <tr><td>dash</td><td>one of "none", "solid", "s-dot", "s-dash-dot", "s-dash-dot-dot", "dash-dot-dot-dot", "dot",
+ * "s-dash", "dash", "l-dash", "dash-dot", or "dash-dot-dot"</td></tr>
+ * <tr><td>color</td><td>any color as described above</td></tr>
+ * <tr><td>width</td><td>float</td><td>not always taken into account</td></tr>
+ * </table>
+ *
+ * "fill" node
+ *
+ * contents: (pattern|gradient)?
+ * <table style="border-spacing:1cm 2mm">
+ * <thead><tr><td>attribute</td><td>value</td><td>comments</td></tr></thead>
+ * <tr><td>type</td><td>one of "none", "pattern", or "gradient"</td></tr>
+ * </table>
+ *
+ * "pattern" node
+ *
+ * Should be included in the fill node if type is pattern.
+ * <table style="border-spacing:1cm 2mm">
+ * <thead><tr><td>attribute</td><td>value</td></tr></thead>
+ * <tr><td>type</td><td>one of "solid", "grey75", "grey50", "grey25", "grey12.5",
+ * "grey6.25", "horiz", "vert", "rev-diag", "diag", "diag-cross", "thick-diag-cross",
+ * "thin-horiz", "thin-vert", "thin-rev-diag", "thin-diag", "thin-horiz-cross",
+ * "thin-diag-cross", "foreground-solid", "small-circles","semi-circles", "thatch",
+ * "large-circles", or "bricks"</td></tr>
+ * <tr><td>fore</td><td>any color as described above</td></tr>
+ * <tr><td>back</td><td>any color as described above</td></tr>
+ * </table>
+ *
+ * "gradient" node
+ *
+ * Should be included in the fill node if type is gradient.
+ * <table style="border-spacing:1cm 2mm">
+ * <thead><tr><td>attribute</td><td>value</td><td>comments</td></tr></thead>
+ * <tr><td>direction</td><td>one of "n-s", "s-n", "n-s-mirrored", "s-n-mirrored",
+ * "w-e", "e-w", "w-e-mirrored", "e-w-mirrored", "nw-se", "se-nw", "nw-se-mirrored",
+ * "se-nw-mirrored", "ne-sw", "sw-ne", "sw-ne-mirrored", or "ne-sw-mirrored" </td><td></td></tr>
+ * <tr><td>start_color</td><td>any color as described above</td><td></td></tr>
+ * <tr><td>brightness</td><td>float</td><td>meaningful only for monocolor gradients</td></tr>
+ * <tr><td>end_color</td><td>any color as described above</td><td>meaningful only for bicolor gradients</td></tr>
+ * </table>
+ *
+ * "marker" node
+ *
+ * <table style="border-spacing:1cm 2mm">
+ * <thead><tr><td>attribute</td><td>value</td><td>comments</td></tr></thead>
+ * <tr><td>shape</td><td>one of "none", "square", "diamond", "triangle-down",
+ * "triangle-up", "triangle-right", "triangle-left", "circle", "x", "cross",
+ * "asterisk", "bar", "half-bar", "butterfly", "hourglass", or "lefthalf-bar"</td><td></td></tr>
+ * <tr><td>fill-color</td><td>any color as described above</td><td></td></tr>
+ * <tr><td>outline-color</td><td>any color as described above</td><td></td></tr>
+ * <tr><td>size</td><td>float</td><td>not always taken into account</td></tr>
+ * </table>
+ *
+ * "font" node
+ * <table style="border-spacing:1cm 2mm">
+ * <thead><tr><td>attribute</td><td>value</td></tr></thead>
+ * <tr><td>color</td><td>any color as described above</td></tr>
+ * <tr><td>font</td><td>a string describing the font such as "Sans 10"</td></tr>
+ * </table>
+ *
+ * "text_layout" node
+ * <table style="border-spacing:1cm 2mm">
+ * <thead><tr><td>attribute</td><td>value</td><td>comments</td></tr></thead>
+ * <tr><td>angle</td><td>float</td><td>expressed in degrees</td></tr>
+ * </table>
+ **/
+
+typedef void (*GogThemeStyleMap) (GOStyle *style, unsigned ind, GogTheme const *theme);
typedef struct {
/* If role is non-null, klass_name specifies the container class,
@@ -46,11 +178,13 @@ struct _GogTheme {
GObject base;
char *name;
- char *load_from_file; /* optionally NULL */
+ char *local_name;
+ char *description;
GHashTable *elem_hash_by_role;
GHashTable *elem_hash_by_class;
GHashTable *class_aliases;
GOStyle *default_style;
+ GArray *palette;
};
typedef GObjectClass GogThemeClass;
@@ -90,15 +224,22 @@ static void
gog_theme_finalize (GObject *obj)
{
GogTheme *theme = GOG_THEME (obj);
+ unsigned i;
g_free (theme->name); theme->name = NULL;
- g_free (theme->load_from_file); theme->load_from_file = NULL;
+ g_free (theme->local_name); theme->local_name = NULL;
+ g_free (theme->description); theme->description = NULL;
if (theme->elem_hash_by_role)
g_hash_table_destroy (theme->elem_hash_by_role);
if (theme->elem_hash_by_class)
g_hash_table_destroy (theme->elem_hash_by_class);
if (theme->class_aliases)
g_hash_table_destroy (theme->class_aliases);
+ if (theme->palette) {
+ for (i = 0; i < theme->palette->len; i++)
+ g_object_unref (g_array_index (theme->palette, GObject*, i));
+ g_array_unref (theme->palette);
+ }
(parent_klass->finalize) (obj);
}
@@ -132,6 +273,7 @@ GSF_CLASS (GogTheme, gog_theme,
gog_theme_class_init, gog_theme_init,
G_TYPE_OBJECT)
+
static GogThemeElement *
gog_theme_find_element (GogTheme const *theme, GogObject const *obj)
{
@@ -143,11 +285,6 @@ gog_theme_find_element (GogTheme const *theme, GogObject const *obj)
theme = default_theme;
g_return_val_if_fail (theme != NULL, NULL);
- if (theme->load_from_file != NULL) {
- /* FIXME: Parse some XML */
- g_warning ("[GogTheme] Theme from XML file not implemented");
- }
-
/* FIXME: Restore hash search caching. */
/* Search by role */
@@ -174,6 +311,7 @@ gog_theme_find_element (GogTheme const *theme, GogObject const *obj)
klass = G_OBJECT_GET_CLASS (obj);
do {
name = G_OBJECT_CLASS_NAME (klass);
+
elem = g_hash_table_lookup ( /* Is the type known ? */
theme->elem_hash_by_class, name);
if (elem == NULL) { /* Is this a local alias ? */
@@ -195,6 +333,10 @@ gog_theme_find_element (GogTheme const *theme, GogObject const *obj)
(klass = g_type_class_peek_parent (klass)) != NULL);
}
+ /* still not found, use default theme */
+ if (elem == NULL && theme != default_theme)
+ elem = gog_theme_find_element (default_theme, obj);
+
return elem;
}
@@ -232,12 +374,12 @@ gog_theme_fillin_style (GogTheme const *theme,
/* ensure only relevant fields are changed */
GOStyleFlag flags = style->disable_theming;
style->disable_theming = GO_STYLE_ALL ^ relevant_fields;
- (elem->map) (style, ind);
+ (elem->map) (style, ind, theme);
style->disable_theming = flags;
}
}
-GogTheme *
+static GogTheme *
gog_theme_new (char const *name)
{
GogTheme *theme = g_object_new (GOG_TYPE_THEME, NULL);
@@ -245,14 +387,6 @@ gog_theme_new (char const *name)
return theme;
}
-GogTheme *
-gog_theme_new_from_file (char const *name, char const *file)
-{
- GogTheme *theme = gog_theme_new (name);
- theme->load_from_file = g_strdup (file);
- return theme;
-}
-
#if 0
void
gog_theme_register_file (char const *name, char const *file)
@@ -284,13 +418,24 @@ gog_theme_add_element (GogTheme *theme, GOStyle *style,
elem->map = map;
/* Never put an element into both by_role_id & by_class_name */
- if (role_id != NULL)
- g_hash_table_insert (theme->elem_hash_by_role,
- (gpointer)elem, elem);
- else if (klass_name != NULL)
- g_hash_table_insert (theme->elem_hash_by_class,
- (gpointer)klass_name, elem);
- else {
+ if (role_id != NULL) {
+ if (g_hash_table_lookup (theme->elem_hash_by_role, (gpointer)elem) == NULL)
+ g_hash_table_insert (theme->elem_hash_by_role,
+ (gpointer)elem, elem);
+ else {
+ g_object_unref (style);
+ g_free (elem);
+ }
+ } else if (klass_name != NULL) {
+ if (g_hash_table_lookup (theme->elem_hash_by_class, (gpointer)klass_name) == NULL)
+ g_hash_table_insert (theme->elem_hash_by_class,
+ (gpointer)klass_name, elem);
+ else {
+ g_object_unref (style);
+ g_free (elem);
+ }
+
+ } else {
if (theme->default_style)
g_object_unref (theme->default_style);
theme->default_style = style;
@@ -298,6 +443,13 @@ gog_theme_add_element (GogTheme *theme, GOStyle *style,
}
}
+/**
+ * gog_theme_get_name:
+ * @theme: a #GogTheme
+ *
+ * Returns: the GogTheme name.
+ **/
+
char const *
gog_theme_get_name (GogTheme const *theme)
{
@@ -305,6 +457,34 @@ gog_theme_get_name (GogTheme const *theme)
return theme->name;
}
+/**
+ * gog_theme_get_local_name:
+ * @theme: a #GogTheme
+ *
+ * Returns: the localized GogTheme name.
+ **/
+
+char const *
+gog_theme_get_local_name (GogTheme const *theme)
+{
+ g_return_val_if_fail (GOG_IS_THEME (theme), "");
+ return (theme->local_name)? theme->local_name: _(theme->name);
+}
+
+/**
+ * gog_theme_get_descrition:
+ * @theme: a #GogTheme
+ *
+ * Returns: the localized GogTheme decription.
+ **/
+
+char const *
+gog_theme_get_description (GogTheme const *theme)
+{
+ g_return_val_if_fail (GOG_IS_THEME (theme), "");
+ return theme->description;
+}
+
/**************************************************************************/
static void
@@ -341,7 +521,7 @@ map_marker (GOStyleMark *mark, unsigned shape, unsigned palette_index,
}
static void
-map_area_series_solid_default (GOStyle *style, unsigned ind)
+map_area_series_solid_default (GOStyle *style, unsigned ind, G_GNUC_UNUSED GogTheme const *theme)
{
static GOColor const palette [] = {
0x9c9cffff, 0x9c3163ff, 0xffffceff, 0xceffffff, 0x630063ff,
@@ -380,7 +560,7 @@ map_area_series_solid_default (GOStyle *style, unsigned ind)
}
static void
-map_area_series_solid_guppi (GOStyle *style, unsigned ind)
+map_area_series_solid_guppi (GOStyle *style, unsigned ind, G_GNUC_UNUSED GogTheme const *theme)
{
static GOColor const palette[] = {
0xff3000ff, 0x80ff00ff, 0x00ffcfff, 0x2000ffff,
@@ -410,6 +590,20 @@ map_area_series_solid_guppi (GOStyle *style, unsigned ind)
map_marker (&style->marker, ind, palette_index, palette);
}
+static void
+map_area_series_solid_palette (GOStyle *style, unsigned ind, GogTheme const *theme)
+{
+ GOStyle *src;
+ if (theme->palette->len == 0)
+ src = theme->default_style;
+ else {
+ ind %= theme->palette->len;
+ src = g_array_index (theme->palette, GOStyle*, ind);
+ }
+ if (src)
+ go_style_apply_theme (style, src, style->interesting_fields);
+}
+
/**************************************************************************/
/**
@@ -420,7 +614,7 @@ map_area_series_solid_guppi (GOStyle *style, unsigned ind)
* Keep a pointer to @theme in graph theme registry.
* This function does not add a reference to @theme.
**/
-void
+static void
gog_theme_registry_add (GogTheme *theme, gboolean is_default)
{
g_return_if_fail (GOG_IS_THEME (theme));
@@ -483,8 +677,7 @@ gog_theme_registry_get_theme_names (void)
/**************************************************************************/
-void
-_gog_themes_init (void)
+static void build_predefined_themes (void)
{
GogTheme *theme;
GOStyle *style;
@@ -494,6 +687,8 @@ _gog_themes_init (void)
g_str_hash, g_str_equal);
g_hash_table_insert (global_class_aliases,
(gpointer)"GogSeriesElement", (gpointer)"GogSeries");
+ g_hash_table_insert (global_class_aliases,
+ (gpointer)"GogSeriesLine", (gpointer)"GogSeries");
}
/* An MS Excel-ish theme */
@@ -627,11 +822,6 @@ _gog_themes_init (void)
NULL, "GogEquation", NULL);
#endif
- /* series lines */
- style = go_style_new ();
- gog_theme_add_element (theme, style,
- map_area_series_solid_default, "GogSeriesLines", NULL);
-
/* Guppi */
theme = gog_theme_new (N_("Guppi"));
gog_theme_registry_add (theme, FALSE);
@@ -756,11 +946,179 @@ _gog_themes_init (void)
gog_theme_add_element (theme, style,
NULL, "GogEquation", NULL);
#endif
+}
+
+struct theme_load_state {
+ GogTheme *theme;
+ char *desc, *lang, *local_name;
+ unsigned name_lang_score;
+ unsigned desc_lang_score;
+ char const * const *langs;
+};
- /* series lines */
+static void
+name_start (GsfXMLIn *xin, xmlChar const **attrs)
+{
+ struct theme_load_state *state = (struct theme_load_state *) xin->user_state;
+ unsigned i;
+ for (i = 0; attrs != NULL && attrs[i] && attrs[i+1] ; i += 2)
+ if (0 == strcmp (attrs[i], "xml:lang"))
+ state->lang = g_strdup (attrs[i+1]);
+}
+
+static void
+name_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
+{
+ struct theme_load_state *state = (struct theme_load_state *) xin->user_state;
+ char *name = NULL;
+ if (xin->content->str == NULL)
+ return;
+ name = g_strdup (xin->content->str);
+ if (state->lang == NULL) {
+ GOStyle *style;
+ state->theme = gog_theme_new (name);
+ state->theme->palette = g_array_new (FALSE, FALSE, sizeof (GOStyle*));
+ /* initialize a dummy GogSeries style */
+ style = go_style_new ();
+ style->line.dash_type = GO_LINE_SOLID;
+ style->line.width = 0; /* hairline */
+ style->line.color = GO_COLOR_BLACK;
+ style->fill.type = GO_STYLE_FILL_NONE;
+ gog_theme_add_element (state->theme, style,
+ map_area_series_solid_palette, "GogSeries", NULL);
+ } else if (state->name_lang_score > 0 && state->langs[0] != NULL) {
+ unsigned i;
+ for (i = 0; i < state->name_lang_score && state->langs[i] != NULL; i++) {
+ if (strcmp (state->langs[i], state->lang) == 0) {
+ g_free (state->local_name);
+ state->local_name = g_strdup (name);
+ state->name_lang_score = i;
+ }
+ }
+ }
+ g_free (state->lang);
+ state->lang = NULL;
+}
+
+static void
+desc_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
+{
+ struct theme_load_state *state = (struct theme_load_state *) xin->user_state;
+ if (state->lang == NULL) {
+ if (state->desc == NULL)
+ state->desc = g_strdup (xin->content->str);
+ } else if (state->desc_lang_score > 0 && state->langs[0] != NULL) {
+ unsigned i;
+ for (i = 0; i < state->desc_lang_score && state->langs[i] != NULL; i++) {
+ if (strcmp (state->langs[i], state->lang) == 0) {
+ g_free (state->desc);
+ state->desc = g_strdup (xin->content->str);
+ state->desc_lang_score = i;
+ }
+ }
+ }
+ g_free (state->lang);
+ state->lang = NULL;
+}
+
+static void
+elem_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
+{
+ struct theme_load_state *state = (struct theme_load_state *) xin->user_state;
+ char const *role = NULL, *class_name = NULL;
+ GOStyle *style;
+ unsigned i;
+
+ if (state->theme == NULL)
+ return;
+ for (i = 0; attrs != NULL && attrs[i] && attrs[i+1] ; i += 2)
+ if (0 == strcmp (attrs[i], "class"))
+ class_name = g_strdup (attrs[i+1]);
+ else if (0 == strcmp (attrs[i], "role"))
+ role = g_strdup (attrs[i+1]);
style = go_style_new ();
- gog_theme_add_element (theme, style,
- NULL, "GogSeriesLines", NULL);
+ go_persist_prep_sax (GO_PERSIST (style), xin, attrs);
+
+ if (class_name && !strcmp (class_name, "GogSeries"))
+ state->theme->palette = g_array_append_val (state->theme->palette, style);
+ else
+ gog_theme_add_element (state->theme, style, NULL, class_name, role);
+}
+
+static void
+theme_load_from_uri (char const *uri)
+{
+ static GsfXMLInNode const theme_dtd[] = {
+ GSF_XML_IN_NODE (THEME, THEME, -1, "GogTheme", GSF_XML_NO_CONTENT, NULL, NULL),
+ GSF_XML_IN_NODE (THEME, NAME, -1, "name", GSF_XML_CONTENT, name_start, name_end),
+ GSF_XML_IN_NODE (THEME, UNAME, -1, "_name", GSF_XML_CONTENT, name_start, name_end),
+ GSF_XML_IN_NODE (THEME, DESCRIPTION, -1, "description", GSF_XML_CONTENT, name_start, desc_end),
+ GSF_XML_IN_NODE (THEME, UDESCRIPTION, -1, "_description", GSF_XML_CONTENT, name_start, desc_end),
+ GSF_XML_IN_NODE (THEME, STYLE, -1, "GOStyle", GSF_XML_NO_CONTENT, elem_start, NULL),
+ GSF_XML_IN_NODE_END
+ };
+ struct theme_load_state state;
+ GsfXMLInDoc *xml;
+ GsfInput *input = go_file_open (uri, NULL);
+
+ if (input == NULL)
+ g_warning ("[GogTheme]: Could not open %s", uri);
+ state.theme = NULL;
+ state.desc = state.lang = state.local_name = NULL;
+ state.langs = g_get_language_names ();
+ state.name_lang_score = state.desc_lang_score = G_MAXINT;
+ xml = gsf_xml_in_doc_new (theme_dtd, NULL);
+ if (!gsf_xml_in_doc_parse (xml, input, &state))
+ g_warning ("[GogTheme]: Could not parse %s", uri);
+ if (state.theme != NULL) {
+ state.theme->local_name = state.local_name;
+ state.theme->description = state.desc;
+ gog_theme_registry_add (state.theme, FALSE);
+ } else {
+ g_free (state.local_name);
+ g_free (state.desc);
+ }
+ g_free (state.lang);
+ gsf_xml_in_doc_free (xml);
+ g_object_unref (input);
+}
+
+static void
+themes_load_from_dir (char const *path)
+{
+ GDir *dir = g_dir_open (path, 0, NULL);
+ char const *d_name;
+ char *uri, *mime_type;
+
+ if (dir == NULL)
+ return;
+ while ((d_name = g_dir_read_name (dir)) != NULL) {
+ uri = g_strconcat ("file://", path, "/", d_name, NULL);
+ mime_type = go_get_mime_type (uri);
+ if (!strcmp (mime_type, "application/x-theme"))
+ theme_load_from_uri (uri);
+ g_free (mime_type);
+ g_free (uri);
+ }
+ g_dir_close (dir);
+}
+
+void
+_gog_themes_init (void)
+{
+ char *path;
+ char const *home;
+
+ build_predefined_themes ();
+
+ /* Load themes from file */
+ path = g_build_filename (go_sys_data_dir (), "themes", NULL);
+ themes_load_from_dir (path);
+ g_free (path);
+ home = getenv ("HOME");
+ path = g_strconcat (home, "/.goffice/themes", NULL);
+ themes_load_from_dir (path);
+ g_free (path);
}
void
diff --git a/goffice/graph/gog-theme.h b/goffice/graph/gog-theme.h
index 5ff8bdb..be7644f 100644
--- a/goffice/graph/gog-theme.h
+++ b/goffice/graph/gog-theme.h
@@ -31,14 +31,13 @@ G_BEGIN_DECLS
GType gog_theme_get_type (void);
-GogTheme *gog_theme_new (char const *name);
-GogTheme *gog_theme_new_from_file (char const *name, char const *file);
char const *gog_theme_get_name (GogTheme const *theme);
+char const *gog_theme_get_local_name (GogTheme const *theme);
+char const *gog_theme_get_description (GogTheme const *theme);
void gog_theme_fillin_style (GogTheme const *theme, GOStyle *style,
GogObject const *obj, int ind,
GOStyleFlag relevant_fields);
-void gog_theme_registry_add (GogTheme *theme, gboolean is_default);
GogTheme *gog_theme_registry_lookup (char const *name);
GSList *gog_theme_registry_get_theme_names (void);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]