[goffice] Implement color map change in color and contour plots



commit 2e10be931a84474e98cb1421fcc2ae0d5acc6554
Author: Jean Brefort <jean brefort normalesup org>
Date:   Thu Nov 1 15:03:05 2012 +0100

    Implement color map change in color and contour plots

 ChangeLog                                 |   22 ++
 docs/reference/goffice-0.10-sections.txt  |    9 +
 docs/reference/goffice-docs.xml           |    1 +
 goffice/Makefile.am                       |    1 +
 goffice/app/go-doc.c                      |   43 +++
 goffice/app/go-doc.h                      |    5 +
 goffice/app/goffice-app.h                 |    5 +
 goffice/goffice.h                         |    8 -
 goffice/graph/goffice-graph.h             |    2 +
 goffice/graph/gog-axis-color-map-prefs.ui |  248 ++++++++++++++++++
 goffice/graph/gog-axis-color-map.c        |  403 ++++++++++++++++++++++++++++-
 goffice/graph/gog-axis-color-map.h        |   12 +
 goffice/graph/gog-axis-prefs.ui           |  182 ++++++++------
 goffice/graph/gog-axis.c                  |  135 ++++++++++-
 goffice/graph/gog-color-scale.c           |   12 +-
 goffice/graph/gog-graph.c                 |   18 +-
 goffice/graph/gog-graph.h                 |    1 +
 goffice/graph/gog-object-xml.c            |   11 +-
 goffice/graph/gog-object.c                |    4 +-
 goffice/graph/gog-theme.c                 |   25 ++-
 goffice/gtk/go-locale-sel.c               |    2 +-
 goffice/utils/go-path.c                   |    2 +-
 plugins/plot_surface/gog-contour.c        |   16 +-
 po/POTFILES.in                            |    2 +
 24 files changed, 1050 insertions(+), 119 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 811b28b..f990b48 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2012-11-01  Jean Brefort  <jean brefort normalesup org>
+
+	* goffice/Makefile.am:  add new ui file.
+	* file goffice/app/go-doc.c: add support for color maps.
+	* file goffice/app/go-doc.h: ditto.
+	* file goffice/app/goffice-app.h: ditto.
+	* file goffice/goffice.h: ditto.
+	* file goffice/graph/goffice-graph.h: ditto.
+	* goffice/graph/gog-axis-color-map-prefs.ui: new ui file.
+	* goffice/graph/gog-axis-color-map.c: new functions.
+	* goffice/graph/gog-axis-color-map.h: ditto.
+	* goffice/graph/gog-axis-prefs.ui: allow for color map changes.
+	* goffice/graph/gog-axis.c: ditto.
+	* goffice/graph/gog-color-scale.c: minor update.
+	* goffice/graph/gog-graph.c: make sure we can access the GODoc when loading.
+	* goffice/graph/gog-graph.h: ditto.
+	* goffice/graph/gog-object-xml.c: ditto.
+	* goffice/graph/gog-object.c: ditto.
+	* goffice/graph/gog-theme.c: minor update.
+	* plugins/plot_surface/gog-contour.c: interpolate colors when using a color
+	map with less colors than there are levels.
+
 2012-10-28  Morten Welinder  <terra gnome org>
 
 	* goffice/math/go-regression.c (go_regression_stat_destroy): Plug
diff --git a/docs/reference/goffice-0.10-sections.txt b/docs/reference/goffice-0.10-sections.txt
index 02af549..8f53a72 100644
--- a/docs/reference/goffice-0.10-sections.txt
+++ b/docs/reference/goffice-0.10-sections.txt
@@ -843,6 +843,8 @@ go_distribution_get_type
 GODoc
 go_doc_add_image
 go_doc_end_read
+go_doc_foreach_color_map
+go_doc_get_color_map
 go_doc_get_dirty_time
 go_doc_get_image
 go_doc_get_images
@@ -2741,9 +2743,15 @@ gog_axis_base_view_get_type
 <FILE>gog-axis-color-map</FILE>
 <TITLE>GogAxisColorMap</TITLE>
 GogAxisColorMap
+gog_axis_color_map_edit
+GogAxisColorMapHandler
+gog_axis_color_map_foreach
 gog_axis_color_map_from_colors
 gog_axis_color_map_get_color
+gog_axis_color_map_get_from_name
 gog_axis_color_map_get_max
+gog_axis_color_map_get_name
+gog_axis_color_map_get_snapshot
 <SUBSECTION Standard>
 GOG_AXIS_COLOR_MAP
 GOG_IS_AXIS_COLOR_MAP
@@ -2939,6 +2947,7 @@ gog_graph_dup
 gog_graph_export_image
 gog_graph_force_update
 gog_graph_get_data
+gog_graph_get_document
 gog_graph_get_size
 gog_graph_get_supported_image_formats
 gog_graph_get_theme
diff --git a/docs/reference/goffice-docs.xml b/docs/reference/goffice-docs.xml
index 8567871..5d6185b 100644
--- a/docs/reference/goffice-docs.xml
+++ b/docs/reference/goffice-docs.xml
@@ -51,6 +51,7 @@
 			<xi:include href="xml/gog-theme.xml"/>
 			<xi:include href="xml/gog-chart-map.xml"/>
 			<xi:include href="xml/gog-axis-map.xml"/>
+			<xi:include href="xml/gog-axis-color-map.xml"/>
 			<xi:include href="xml/gog-3d-box.xml"/>
 		</chapter>
 	</part>
diff --git a/goffice/Makefile.am b/goffice/Makefile.am
index 6878d91..9c8d744 100644
--- a/goffice/Makefile.am
+++ b/goffice/Makefile.am
@@ -554,6 +554,7 @@ endif
 embedded_stuff_compress = \
 	graph/gog-3d-box-prefs.ui		\
 	graph/gog-axis-prefs.ui			\
+	graph/gog-axis-color-map-prefs.ui	\
 	graph/gog-equation-prefs.ui		\
 	graph/gog-error-bar-prefs.ui		\
 	graph/gog-graph-prefs.ui		\
diff --git a/goffice/app/go-doc.c b/goffice/app/go-doc.c
index 49040ec..25997fd 100644
--- a/goffice/app/go-doc.c
+++ b/goffice/app/go-doc.c
@@ -33,6 +33,10 @@
 
 struct _GODocPrivate {
 	GHashTable	*imagebuf; /* used when loading/saving images */
+
+	/* color maps */
+	GSList	*colormaps; /* document graph axis color maps */
+	GSList	*colormapsbuf; /* used when loading/saving color maps */
 };
 
 /**
@@ -132,6 +136,7 @@ go_doc_finalize (GObject *obj)
 	if (doc->images)
 		g_hash_table_destroy (doc->images);
 	doc->images = NULL;
+	g_slist_free_full (doc->priv->colormaps, g_object_unref);
 	g_free (doc->priv);
 	doc->priv = NULL;
 
@@ -627,3 +632,41 @@ go_doc_image_fetch (GODoc *doc, char const *id, GType type)
 	}
 	return image;
 }
+
+/**
+ * go_doc_get_color_map:
+ * @doc: a #GODoc
+ * @name: the color map name to search for
+ *
+ * Retrieves the color map whose name is @name. The difference with
+ * gog_axis_color_map_get_from_name() is that color maps specific to the
+ * document are searched first if any.
+ * Returns: (transfer none): the found color map or %NULL.
+ **/
+GogAxisColorMap	const *
+go_doc_get_color_map (GODoc *doc, char const *name)
+{
+	g_return_val_if_fail (GO_IS_DOC (doc) && name && *name, NULL);
+#if 0
+	if (doc->priv->colormaps == NULL)
+		return NULL;
+	return g_hash_table_lookup (doc->priv->colormaps, name);
+#endif
+	return gog_axis_color_map_get_from_name (name);
+}
+
+/**
+ * go_doc_foreach_color_map:
+ * @doc: a #GODoc
+ * @handler: (scope call): a #GogAxisColorMapHandler
+ * @user_data: data to pass to @handler
+ *
+ * Executes @handler to each color map installed on the system or specific to
+ * @doc. This function calls gog_axis_color_map_foreach() but don't execute the
+ * handler if a color map with the same name exists in the document.
+ **/
+void
+go_doc_foreach_color_map (GODoc *doc, GogAxisColorMapHandler handler, gpointer user_data)
+{
+	gog_axis_color_map_foreach (handler, user_data);
+}
diff --git a/goffice/app/go-doc.h b/goffice/app/go-doc.h
index d866fdf..1c1a14b 100644
--- a/goffice/app/go-doc.h
+++ b/goffice/app/go-doc.h
@@ -62,6 +62,11 @@ void		 go_doc_read		(GODoc *doc, GsfXMLIn *xin, xmlChar const **attrs);
 void		 go_doc_end_read	(GODoc *doc);
 GOImage		*go_doc_image_fetch 	(GODoc *doc, char const *id, GType type);
 
+GogAxisColorMap	const *go_doc_get_color_map	(GODoc *doc, char const *name);
+void		 go_doc_foreach_color_map	(GODoc *doc,
+			                             GogAxisColorMapHandler handler,
+			                             gpointer user_data);
+
 G_END_DECLS
 
 #endif /* GO_DOC_H */
diff --git a/goffice/app/goffice-app.h b/goffice/app/goffice-app.h
index 1ce7b03..5edf2d8 100644
--- a/goffice/app/goffice-app.h
+++ b/goffice/app/goffice-app.h
@@ -61,6 +61,11 @@ G_END_DECLS
 
 #include <goffice/goffice.h>
 #include <goffice/utils/goffice-utils.h>
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/canvas/goffice-canvas.h>
+#ifdef GOFFICE_WITH_GTK
+#include <goffice/gtk/goffice-gtk.h>
+#endif
 
 #include <goffice/app/error-info.h>
 #include <goffice/app/file.h>
diff --git a/goffice/goffice.h b/goffice/goffice.h
index 6dbfa8b..bf15e1a 100644
--- a/goffice/goffice.h
+++ b/goffice/goffice.h
@@ -56,14 +56,6 @@
 #endif
 
 #include <goffice/app/goffice-app.h>
-#include <goffice/utils/goffice-utils.h>
-#include <goffice/data/goffice-data.h>
-#include <goffice/math/goffice-math.h>
-#include <goffice/graph/goffice-graph.h>
-#include <goffice/canvas/goffice-canvas.h>
-#ifdef GOFFICE_WITH_GTK
-#include <goffice/gtk/goffice-gtk.h>
-#endif
 
 G_BEGIN_DECLS
 
diff --git a/goffice/graph/goffice-graph.h b/goffice/graph/goffice-graph.h
index b550056..ff50a8b 100644
--- a/goffice/graph/goffice-graph.h
+++ b/goffice/graph/goffice-graph.h
@@ -236,6 +236,8 @@ extern int goffice_graph_debug_level;
 G_END_DECLS
 
 #include <goffice/goffice.h>
+#include <goffice/data/goffice-data.h>
+#include <goffice/math/goffice-math.h>
 
 #include <goffice/graph/gog-object.h>
 #include <goffice/graph/gog-styled-object.h>
diff --git a/goffice/graph/gog-axis-color-map-prefs.ui b/goffice/graph/gog-axis-color-map-prefs.ui
new file mode 100644
index 0000000..fb33bae
--- /dev/null
+++ b/goffice/graph/gog-axis-color-map-prefs.ui
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkAdjustment" id="stop-adj">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkDialog" id="gog-axis-color-map-prefs">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="button2">
+                <property name="label">gtk-close</property>
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button1">
+                <property name="label">gtk-save</property>
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGrid" id="grid">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="vexpand">True</property>
+            <property name="row_spacing">6</property>
+            <property name="column_spacing">12</property>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">&lt;b&gt;Current color stop&lt;/b&gt;</property>
+                <property name="use_markup">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+                <property name="width">4</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_left">12</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Value</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">2</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Color</property>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="top_attach">2</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label4">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_top">6</property>
+                <property name="xalign">0</property>
+                <property name="ypad">5</property>
+                <property name="label" translatable="yes">&lt;b&gt;Snapshots&lt;/b&gt;</property>
+                <property name="use_markup">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label5">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_left">12</property>
+                <property name="vexpand">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Discrete</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">4</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label6">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_left">12</property>
+                <property name="vexpand">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Gradient</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">5</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSpinButton" id="stop-btn">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">â</property>
+                <property name="adjustment">stop-adj</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">2</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label7">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_left">12</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Name</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="name">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">â</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">0</property>
+                <property name="width">3</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">button2</action-widget>
+      <action-widget response="1">button1</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/goffice/graph/gog-axis-color-map.c b/goffice/graph/gog-axis-color-map.c
index 8c2508a..9bf7a86 100644
--- a/goffice/graph/gog-axis-color-map.c
+++ b/goffice/graph/gog-axis-color-map.c
@@ -21,16 +21,32 @@
 
 #include <goffice/goffice-config.h>
 #include <goffice/goffice.h>
+#include <goffice/goffice-priv.h>
 
 #include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
 
 #ifdef GOFFICE_WITH_GTK
 #include <gtk/gtk.h>
 #endif
 
+/**
+ * SECTION: gog-axis-color-map
+ * @short_description: map values to colors.
+ *
+ * Used to map color and pseudo-3d axes values to the actual color. The first
+ * color maps 0 and the last a positive integer returned by
+ * gog_axis_color_map_get_max(). For color axes, these integer values must
+ * themselves be mapped to the minimum and maximum of the axis (unless the
+ * axis is inverted). For pseudo-3d axes, successive colors are obtained for
+ * integer values, cycling to the first color when the colors number is not
+ * large enough.
+ **/
 struct _GogAxisColorMap {
 	GObject base;
-	char *name;
+	char *name, *local_name;
+	GHashTable *names;
 	unsigned size; /* colors number */
 	unsigned *limits;
 	GOColor *colors;
@@ -45,10 +61,15 @@ gog_axis_color_map_finalize (GObject *obj)
 	GogAxisColorMap *map = GOG_AXIS_COLOR_MAP (obj);
 	g_free (map->name);
 	map->name = NULL;
+	g_free (map->local_name);
+	map->local_name = NULL;
 	g_free (map->limits);
 	map->limits = NULL;
 	g_free (map->colors);
 	map->colors = NULL;
+	if (map->names)
+		g_hash_table_destroy (map->names);
+	map->names = NULL;
 	parent_klass->finalize (obj);
 }
 
@@ -69,6 +90,14 @@ GSF_CLASS (GogAxisColorMap, gog_axis_color_map,
 	   gog_axis_color_map_class_init, gog_axis_color_map_init,
 	   G_TYPE_OBJECT)
 
+/**
+ * gog_axis_color_map_get_color:
+ * @map: a #GogAxisMap
+	 * @x: the value to map
+ *
+ * Maps @x to a color.
+ * Returns: the found color.
+ **/
 GOColor
 gog_axis_color_map_get_color (GogAxisColorMap const *map, double x)
 {
@@ -80,13 +109,21 @@ gog_axis_color_map_get_color (GogAxisColorMap const *map, double x)
 	if (map->size == 1)
 		return map->colors[0];
 	if (x > map->limits[map->size-1])
-		x -= floor (x / map->limits[map->size-1]) * map->limits[map->size-1];
-	while (n < map->size && x > map->limits[n])
+		x -= floor (x / (map->limits[map->size-1] + 1)) * (map->limits[map->size-1] + 1);
+	while (n < map->size && x > map->limits[n] + 1e-10)
 		n++;
 	t = (x - map->limits[n-1]) / (map->limits[n] - map->limits[n-1]);
 	return GO_COLOR_INTERPOLATE (map->colors[n-1], map->colors[n], t);
 }
 
+/**
+ * gog_axis_color_map_get_max:
+ * @map: a #GogAxisMap
+ *
+ * Retrieves the value corresponding to the last color in the map. The first
+ * always corresponds to 0.
+ * Returns: the maximum value.
+ **/
 unsigned
 gog_axis_color_map_get_max (GogAxisColorMap const *map)
 {
@@ -95,6 +132,125 @@ gog_axis_color_map_get_max (GogAxisColorMap const *map)
 }
 
 /**
+ * gog_axis_color_map_get_snapshot:
+ * @map: a #GogAxisMap
+ *
+ * Retrieves the color map name.
+ * Returns: (transfer none): the map name.
+ **/
+char const *
+gog_axis_color_map_get_name (GogAxisColorMap const *map)
+{
+	g_return_val_if_fail (GOG_IS_AXIS_COLOR_MAP (map), NULL);
+	return map->name;
+}
+
+/**
+ * gog_axis_color_map_get_snapshot:
+ * @map: a #GogAxisMap
+ * @discrete: whether to use constant colors between each stop or a gradient.
+ * @horizontal: whether to get an horizontal or a vertical snapshot.
+ * @width: the pixbuf width.
+ * @height: the pixbuf height.
+ *
+ * Builds a snapshot of the color map.
+ * Returns: (transfer full): the new #GdkPixbuf.
+ **/
+GdkPixbuf *
+gog_axis_color_map_get_snapshot (GogAxisColorMap const *map,
+                                 gboolean discrete, gboolean horizontal,
+                                 unsigned width, unsigned height)
+{
+	cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+	GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+	cairo_t *cr = cairo_create (surface);
+	unsigned i, max = gog_axis_color_map_get_max (map);
+
+	g_return_val_if_fail (GOG_IS_AXIS_COLOR_MAP (map), NULL);
+	if (discrete) {
+		GOColor color;
+		unsigned t0 = 0, t1, maxt = horizontal? width: height;
+		max++;
+		for (i = 0; i < max; i++) {
+			t1 = (i + 1) * maxt / max;
+			color = gog_axis_color_map_get_color (map, i);
+			if (horizontal)
+				cairo_rectangle (cr, t0, 0., t1, height);
+			else
+				cairo_rectangle (cr, 0., t0, width, t1);
+			cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (color));
+			cairo_fill (cr);
+			t0 = t1;
+		}
+	} else {
+		double x1, y1;
+		cairo_pattern_t *pattern;
+		if (horizontal) {
+			x1 = width;
+			y1 = 0.;
+		} else {
+			x1 = 0.;
+			y1 = height;
+		}
+		pattern = cairo_pattern_create_linear (0., 0., x1, y1);
+		for (i = 0; i < map->size; i++) {
+			cairo_pattern_add_color_stop_rgba (pattern,
+			                                   (double) map->limits[i] / (double) max,
+			                                   GO_COLOR_TO_CAIRO (map->colors[i]));
+		}
+		cairo_rectangle (cr, 0., 0., width, height);
+		cairo_set_source (cr, pattern);
+		cairo_fill (cr);
+		cairo_pattern_destroy (pattern);
+	}
+
+	go_cairo_convert_data_to_pixbuf (gdk_pixbuf_get_pixels (pixbuf),
+	                                 cairo_image_surface_get_data (surface),
+	                                 width, height, width * 4);
+	cairo_destroy (cr);
+	cairo_surface_destroy (surface);
+	return pixbuf;
+}
+
+#ifdef GOFFICE_WITH_GTK
+/**
+ * gog_axis_color_map_edit:
+ * @map: a #GogAxisColorMap or %NULL
+ * @cc: a #GOCmdContext
+ *
+ * Opens a dialog to edit the color map. If @map is %NULL, creates a new one
+ * unless the user cancels the edition.
+ * Returns: (transfer none): the edited color map.
+ **/
+GogAxisColorMap *
+gog_axis_color_map_edit (GogAxisColorMap *map, GOCmdContext *cc)
+{
+	GtkBuilder *gui = go_gtk_builder_load_internal ("res:go:graph/gog-axis-color-map-prefs.ui", GETTEXT_PACKAGE, cc);
+	GtkWidget *top_level = go_gtk_builder_get_widget (gui, "gog-axis-color-map-prefs");
+	unsigned response;
+	gboolean created = FALSE;
+
+	if (map == NULL) {
+		GOColor color = GO_COLOR_BLUE;
+		map = gog_axis_color_map_from_colors ("New map", 1, &color);
+		created = TRUE;
+	} else {
+	}
+
+	response = gtk_dialog_run (GTK_DIALOG (top_level));
+	if (response == 1) {
+	} else {
+		if (created)
+			g_object_unref (map);
+		map = NULL;
+	}
+	gtk_widget_destroy (top_level);
+	g_object_unref (gui);
+	return map;
+}
+#endif
+
+/**
  * gog_axis_color_map_from_colors:
  * @name: color map name
  * @nb: colors number
@@ -127,12 +283,241 @@ _gog_axis_color_map_get_default ()
 	return color_map;
 }
 
+static GSList *color_maps;
+
+/**
+ * gog_axis_color_map_registry_add:
+ * @map: a #GogAxisColorMap
+ *
+ * Keep a pointer to @map in graph color maps registry.
+ * This function does not add a reference to @map.
+ **/
+static void
+gog_axis_color_map_registry_add (GogAxisColorMap *map)
+{
+	g_return_if_fail (GOG_IS_AXIS_COLOR_MAP (map));
+
+	/* TODO: Check for duplicated names and for already
+	 * registered map */
+
+	color_maps = g_slist_append (color_maps, map);
+}
+
+struct _color_stop {
+	unsigned bin;
+	GOColor color;
+};
+
+struct color_map_load_state {
+	GogAxisColorMap *map;
+	char *lang, *local_name;
+	unsigned name_lang_score;
+	char const * const *langs;
+	GSList *color_stops;
+};
+
+static void
+color_stop_start (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	struct color_map_load_state	*state = (struct color_map_load_state *) xin->user_state;
+	GOColor color;
+	unsigned bin = 0;
+	char *end;
+	gboolean color_found = FALSE;
+	gboolean bin_found = FALSE;
+
+	for (; attrs != NULL && *attrs ; attrs += 2)
+		if (0 == strcmp (*attrs, "bin")) {
+			bin = strtoul (attrs[1], &end, 10);
+			if (*end == 0)
+				bin_found = TRUE;
+		} else if (0 == strcmp (*attrs, "color"))
+			color_found = go_color_from_str (attrs[1], &color);
+	if (color_found && bin_found) {
+		struct _color_stop* stop = g_new (struct _color_stop, 1);
+		stop->bin = bin;
+		stop->color = color;
+		state->color_stops = g_slist_append (state->color_stops, stop);
+	} else
+		g_warning ("[GogAxisColorMap]: Invalid color stop");
+	
+}
+
+static void
+name_start (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	struct color_map_load_state	*state = (struct color_map_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 color_map_load_state	*state = (struct color_map_load_state *) xin->user_state;
+	char *name = NULL;
+	if (xin->content->str == NULL)
+		return;
+	name = g_strdup (xin->content->str);
+	if (state->map == NULL) {
+		state->map = g_object_new (GOG_TYPE_AXIS_COLOR_MAP, NULL);
+		state->map->names = g_hash_table_new_full (g_str_hash, g_str_equal,
+		                                           g_free, g_free);
+	}
+	if (state->lang == NULL) {
+		state->map->name = name;
+	} 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_hash_table_replace (state->map->names, state->lang, name); 
+		state->lang = NULL;
+	}
+}
+
+static int
+color_stops_cmp (struct _color_stop *first, struct _color_stop *second)
+{
+	return (first->bin < second->bin)? -1: (int) (first->bin - second->bin);
+}
+
+static void
+color_map_load_from_uri (char const *uri)
+{
+	static GsfXMLInNode const color_map_dtd[] = {
+		GSF_XML_IN_NODE (THEME, THEME, -1, "GogAxisColorMap", 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, STOP, -1, "color-stop", GSF_XML_CONTENT, color_stop_start, NULL),
+		GSF_XML_IN_NODE_END
+	};
+	struct color_map_load_state	state;
+	GsfXMLInDoc *xml;
+	GsfInput *input = go_file_open (uri, NULL);
+
+	if (input == NULL) {
+		g_warning ("[GogAxisColorMap]: Could not open %s", uri);
+		return;
+	}
+	state.map = NULL;
+	state.lang = state.local_name = NULL;
+	state.langs = g_get_language_names ();
+	state.name_lang_score = G_MAXINT;
+	state.color_stops = NULL;
+	xml = gsf_xml_in_doc_new (color_map_dtd, NULL);
+	if (!gsf_xml_in_doc_parse (xml, input, &state))
+		g_warning ("[GogAxisColorMap]: Could not parse %s", uri);
+	if (state.map != NULL) {
+		GSList *ptr;
+		state.map->local_name = state.local_name;
+		/* populates the colors */
+		/* first sort the color list according to bins */
+		ptr = state.color_stops = g_slist_sort (state.color_stops, (GCompareFunc) color_stops_cmp);
+		if (((struct _color_stop *) ptr->data)->bin != 0) {
+			g_warning ("[GogAxisColorMap]: Invalid color map in %s", uri);
+			g_object_unref (state.map);
+		} else {
+			unsigned cur_bin, n = 0;
+			state.map->size = g_slist_length (state.color_stops);
+			state.map->limits = g_new (unsigned, state.map->size);
+			state.map->colors = g_new (GOColor, state.map->size);
+			while (ptr) {
+				cur_bin = state.map->limits[n] = ((struct _color_stop *) ptr->data)->bin;
+				state.map->colors[n++] = ((struct _color_stop *) ptr->data)->color;
+				do (ptr = ptr->next);
+				while (ptr && ((struct _color_stop *) ptr->data)->bin == cur_bin);
+			}
+			state.map->size = n; /* we drop duplicate bins */
+			gog_axis_color_map_registry_add (state.map);
+		}
+	} else
+		g_free (state.local_name);
+	g_slist_free_full (state.color_stops, g_free);
+	g_free (state.lang);
+	gsf_xml_in_doc_free (xml);
+	g_object_unref (input);
+}
+
+static void
+color_maps_load_from_dir (char const *path)
+{
+	GDir *dir = g_dir_open (path, 0, NULL);
+	char const *d_name;
+
+	if (dir == NULL)
+		return;
+	while ((d_name = g_dir_read_name (dir)) != NULL) {
+		char *fullname = g_build_filename (path, d_name, NULL);
+		char *uri = go_filename_to_uri (fullname);
+		char *mime_type = go_get_mime_type (uri);
+		if (!strcmp (mime_type, "application/xml")) /* we don't have a better one */
+			color_map_load_from_uri (uri);
+		g_free (mime_type);
+		g_free (uri);
+		g_free (fullname);
+	}
+	g_dir_close (dir);
+}
+
+/**
+ * GogAxisColorMapHandler:
+ * @map: a #GogAxisColorMap
+ * @user_data: user data
+ *
+ * Type of the callback to pass to gog_axis_color_map_foreach() and
+ * go_doc_foreach_color_map() to iterate through color maps.
+ **/
+
+/**
+ * gog_axis_color_map_foreach:
+ * @handler: (scope call): a #GogAxisColorMapHandler
+ * @user_data: data to pass to @handler
+ *
+ * Executes @handler to each color map installed on the system. This function
+ * should not be called directly, call go_doc_foreach_color_map() instead.
+ **/
+void
+gog_axis_color_map_foreach (GogAxisColorMapHandler handler, gpointer user_data)
+{
+	GSList *ptr;
+	for (ptr = color_maps; ptr; ptr = ptr->next)
+		handler ((GogAxisColorMap *) (ptr->data), user_data);
+}
+
+/**
+ * gog_axis_color_map_get_from_name:
+ * @name: the color map name to search for
+ *
+ * Retrieves the color map whose name is @name.
+ * Returns: (transfer none): the found color map or %NULL.
+ **/
+GogAxisColorMap const *
+gog_axis_color_map_get_from_name (char const *name)
+{
+	GSList *ptr;
+	for (ptr = color_maps; ptr; ptr = ptr->next)
+		if (!strcmp (((GogAxisColorMap *) (ptr->data))->name, name))
+		    return (GogAxisColorMap *) ptr->data;
+	return NULL;
+}
+
 void
 _gog_axis_color_maps_init (void)
 {
+	char *path;
+
 	/* Default color map */
 	color_map = g_object_new (GOG_TYPE_AXIS_COLOR_MAP, NULL);
-	color_map->name = g_strdup ("Default");
+	color_map->name = g_strdup (N_("Default"));
 	color_map->size = 5;
 	color_map->limits = g_new (unsigned, 5);
 	color_map->colors = g_new (GOColor, 5);
@@ -146,10 +531,20 @@ _gog_axis_color_maps_init (void)
 	color_map->colors[3] = GO_COLOR_YELLOW;
 	color_map->limits[4] = 6;
 	color_map->colors[4] = GO_COLOR_RED;
+
+	/* Now load registered color maps */
+	path = g_build_filename (go_sys_data_dir (), "colormaps", NULL);
+	color_maps_load_from_dir (path);
+	g_free (path);
+
+	path = g_build_filename (g_get_home_dir (), ".goffice", "colormaps", NULL);
+	color_maps_load_from_dir (path);
+	g_free (path);
 }
 
 void
 _gog_axis_color_maps_shutdown (void)
 {
 	g_object_unref (color_map);
+	g_slist_free_full (color_maps, g_object_unref);
 }
diff --git a/goffice/graph/gog-axis-color-map.h b/goffice/graph/gog-axis-color-map.h
index 8607367..093f4eb 100644
--- a/goffice/graph/gog-axis-color-map.h
+++ b/goffice/graph/gog-axis-color-map.h
@@ -35,6 +35,18 @@ GType gog_axis_color_map_get_type (void);
 GOColor gog_axis_color_map_get_color (GogAxisColorMap const *map, double x);
 unsigned gog_axis_color_map_get_max (GogAxisColorMap const *map);
 GogAxisColorMap *gog_axis_color_map_from_colors (char const *name, unsigned nb, GOColor const *colors);
+GdkPixbuf *gog_axis_color_map_get_snapshot (GogAxisColorMap const *map,
+                                            gboolean discrete,
+                                            gboolean horizontal,
+                                            unsigned width,
+                                            unsigned height);
+char const *gog_axis_color_map_get_name (GogAxisColorMap const *map);
+#ifdef GOFFICE_WITH_GTK
+GogAxisColorMap *gog_axis_color_map_edit (GogAxisColorMap *map, GOCmdContext *cc);
+#endif
+typedef void (*GogAxisColorMapHandler) (GogAxisColorMap const *map, gpointer user_data);
+void gog_axis_color_map_foreach (GogAxisColorMapHandler handler, gpointer user_data);
+GogAxisColorMap const *gog_axis_color_map_get_from_name (char const *name);
 
 
 /* private */
diff --git a/goffice/graph/gog-axis-prefs.ui b/goffice/graph/gog-axis-prefs.ui
index f0f48e2..95513f0 100644
--- a/goffice/graph/gog-axis-prefs.ui
+++ b/goffice/graph/gog-axis-prefs.ui
@@ -301,24 +301,6 @@
                 <property name="height">1</property>
               </packing>
             </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
           </object>
           <packing>
             <property name="left_attach">0</property>
@@ -377,24 +359,6 @@
                 <property name="height">1</property>
               </packing>
             </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
           </object>
           <packing>
             <property name="left_attach">0</property>
@@ -403,24 +367,6 @@
             <property name="height">1</property>
           </packing>
         </child>
-        <child>
-          <placeholder/>
-        </child>
-        <child>
-          <placeholder/>
-        </child>
-        <child>
-          <placeholder/>
-        </child>
-        <child>
-          <placeholder/>
-        </child>
-        <child>
-          <placeholder/>
-        </child>
-        <child>
-          <placeholder/>
-        </child>
       </object>
       <packing>
         <property name="left_attach">0</property>
@@ -501,12 +447,6 @@
     <child>
       <placeholder/>
     </child>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
-    </child>
   </object>
   <object class="GtkGrid" id="axis-pref-grid">
     <property name="visible">True</property>
@@ -691,15 +631,6 @@
             <child>
               <placeholder/>
             </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
           </object>
           <packing>
             <property name="left_attach">0</property>
@@ -732,17 +663,118 @@
         <property name="height">1</property>
       </packing>
     </child>
+  </object>
+  <object class="GtkListStore" id="color-map-list">
+    <columns>
+      <!-- column-name Name -->
+      <column type="gchararray"/>
+      <!-- column-name Snapshot -->
+      <column type="GdkPixbuf"/>
+      <!-- column-name Map -->
+      <column type="GObject"/>
+    </columns>
+  </object>
+  <object class="GtkGrid" id="color-map-grid">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">12</property>
+    <property name="row_spacing">6</property>
+    <property name="column_spacing">12</property>
     <child>
-      <placeholder/>
-    </child>
-    <child>
-      <placeholder/>
+      <object class="GtkComboBox" id="color-map-combo">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="hexpand">True</property>
+        <property name="model">color-map-list</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">0</property>
+        <property name="width">2</property>
+        <property name="height">1</property>
+      </packing>
     </child>
     <child>
-      <placeholder/>
+      <object class="GtkLabel" id="label16">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="vexpand">True</property>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">1</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
     </child>
     <child>
-      <placeholder/>
+      <object class="GtkButtonBox" id="buttonbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="spacing">12</property>
+        <property name="layout_style">start</property>
+        <child>
+          <object class="GtkButton" id="new-btn">
+            <property name="label" translatable="yes">New</property>
+            <property name="use_action_appearance">False</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="has_tooltip">True</property>
+            <property name="tooltip_markup" translatable="yes">Creates a new color map from scratch</property>
+            <property name="tooltip_text" translatable="yes">Creates a new color map from scratch</property>
+            <property name="use_action_appearance">False</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="duplicate-btn">
+            <property name="label" translatable="yes">Duplicate</property>
+            <property name="use_action_appearance">False</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="has_tooltip">True</property>
+            <property name="tooltip_markup" translatable="yes">Creates a new color map based on the currently selected one.</property>
+            <property name="tooltip_text" translatable="yes">Creates a new color map based on the currently selected one.</property>
+            <property name="use_action_appearance">False</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="edit-btn">
+            <property name="label" translatable="yes">Edit</property>
+            <property name="use_action_appearance">False</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="has_tooltip">True</property>
+            <property name="tooltip_markup" translatable="yes">Edits the color map.</property>
+            <property name="tooltip_text" translatable="yes">Edits the color map.</property>
+            <property name="use_action_appearance">False</property>
+            <property name="image_position">right</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">0</property>
+        <property name="top_attach">2</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
     </child>
     <child>
       <placeholder/>
diff --git a/goffice/graph/gog-axis.c b/goffice/graph/gog-axis.c
index f90d928..c488c7e 100644
--- a/goffice/graph/gog-axis.c
+++ b/goffice/graph/gog-axis.c
@@ -2054,7 +2054,8 @@ enum {
 	AXIS_PROP_CIRCULAR_ROTATION,
 	AXIS_PROP_POLAR_UNIT,
 	AXIS_PROP_SPAN_START,
-	AXIS_PROP_SPAN_END
+	AXIS_PROP_SPAN_END,
+	AXIS_PROP_COLOR_MAP
 };
 
 /*****************************************************************************/
@@ -2262,6 +2263,24 @@ gog_axis_set_property (GObject *obj, guint param_id,
 			axis->span_end = new_value;
 		}
 		break;
+	case AXIS_PROP_COLOR_MAP: {
+		char const *str = g_value_get_string (value);
+		GogAxisColorMap const *map = NULL;
+		if (strcmp (str, "default")) {
+				map = go_doc_get_color_map (gog_graph_get_document (gog_object_get_graph (GOG_OBJECT (axis))), str);
+			if (!map) {
+				/* might be the continuous theme color map */
+				map = gog_theme_get_color_map (gog_graph_get_theme (gog_object_get_graph (GOG_OBJECT (axis))), FALSE);
+				if (strcmp (gog_axis_color_map_get_name (map), str))
+					map = NULL;
+			}
+		}
+		if (map) {
+			axis->color_map = map;
+			axis->auto_color_map = FALSE;
+		}
+		break;
+	}
 
 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
 		 return; /* NOTE : RETURN */
@@ -2311,6 +2330,9 @@ gog_axis_get_property (GObject *obj, guint param_id,
 	case AXIS_PROP_SPAN_END:
 		g_value_set_double (value, axis->span_end);
 		break;
+	case AXIS_PROP_COLOR_MAP:
+		g_value_set_string (value, (axis->auto_color_map)? "default": gog_axis_color_map_get_name (axis->color_map));
+		break;
 
 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
 		 break;
@@ -2470,6 +2492,8 @@ gog_axis_update (GogObject *obj)
 typedef struct {
 	GogAxis		*axis;
 	GtkWidget 	*format_selector;
+	GtkComboBox *color_map_combo;
+	GOCmdContext *cc;
 } GogAxisPrefState;
 
 static void
@@ -2667,6 +2691,48 @@ cb_end_changed (GtkSpinButton *spin, GogAxis *axis)
 }
 
 static void
+color_map_new_cb (GogAxisPrefState *state)
+{
+	GogAxisColorMap *map = gog_axis_color_map_edit (NULL, state->cc);
+	if (map) {
+	}
+}
+
+static void
+color_map_changed_cb (GtkComboBox *combo, GogAxis *axis)
+{
+	GtkTreeModel *model = gtk_combo_box_get_model (combo);
+	GtkTreeIter iter;
+	GogAxisColorMap *map;
+	GogTheme *theme = gog_graph_get_theme (gog_object_get_graph (GOG_OBJECT (axis)));
+	gtk_combo_box_get_active_iter (combo, &iter);
+	gtk_tree_model_get (model, &iter, 2, &map, -1);
+	axis->color_map = map;
+	axis->auto_color_map = map == gog_theme_get_color_map (theme, axis->type == GOG_AXIS_PSEUDO_3D);
+	gog_object_emit_changed (GOG_OBJECT (axis), FALSE);
+}
+
+struct ColorMapState {
+	GtkComboBox *combo;
+	GtkListStore *model;
+	GogAxisColorMap const *target;
+	gboolean discrete;
+};
+
+static void
+add_color_map_cb (GogAxisColorMap const *map, struct ColorMapState *state)
+{
+	GtkTreeIter iter;
+	GdkPixbuf *pixbuf = gog_axis_color_map_get_snapshot (map, state->discrete,
+		                                          TRUE, 200, 16);
+	gtk_list_store_append (state->model, &iter);
+	gtk_list_store_set (state->model, &iter, 0, gog_axis_color_map_get_name (map),
+	                    1, pixbuf, 2, map, -1);
+	if (map == state->target)
+		gtk_combo_box_set_active_iter (state->combo, &iter);
+}
+
+static void
 gog_axis_populate_editor (GogObject *gobj,
 			  GOEditor *editor,
 			  GogDataAllocator *dalloc,
@@ -2683,13 +2749,13 @@ gog_axis_populate_editor (GogObject *gobj,
 	GogAxisPrefState *state;
 	GogDataset *set = GOG_DATASET (gobj);
 	GtkBuilder *gui;
-
 	gui = go_gtk_builder_load_internal ("res:go:graph/gog-axis-prefs.ui", GETTEXT_PACKAGE, cc);
 	if (gui == NULL)
 		return;
 
 	state = g_new0 (GogAxisPrefState, 1);
 	state->axis = axis;
+	state->cc = cc;
 	g_object_ref (G_OBJECT (axis));
 
 	/* Bounds Page */
@@ -2790,6 +2856,65 @@ gog_axis_populate_editor (GogObject *gobj,
 		                    _("Span"));
 	}
 
+	/* color map */
+	if (axis->type == GOG_AXIS_COLOR || axis->type == GOG_AXIS_PSEUDO_3D) {
+		GtkListStore *model = GTK_LIST_STORE (gtk_builder_get_object (gui, "color-map-list"));
+		GtkTreeIter iter;
+		GdkPixbuf *pixbuf;
+		GogAxisColorMap const *map;
+		GogTheme *theme = gog_graph_get_theme (gog_object_get_graph (GOG_OBJECT (axis)));
+		GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
+		GtkWidget *combo = go_gtk_builder_get_widget (gui, "color-map-combo");
+		struct ColorMapState color_state;
+		state->color_map_combo = GTK_COMBO_BOX (combo);
+		gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
+		gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
+				                        "text", 0, NULL);
+		renderer = gtk_cell_renderer_pixbuf_new ();
+		gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
+		gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
+				                        "pixbuf", 1, NULL);
+
+		if (axis->type == GOG_AXIS_PSEUDO_3D) {
+			gtk_list_store_append (model, &iter);
+			map = gog_theme_get_color_map (theme, TRUE);
+			pixbuf = gog_axis_color_map_get_snapshot (map, TRUE,
+				                                      TRUE, 200, 16);
+			gtk_list_store_set (model, &iter, 0, gog_axis_color_map_get_name (map),
+				                1, pixbuf, 2, map, -1);
+			g_object_unref (pixbuf);
+			if (map == axis->color_map)
+				gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+		}
+		gtk_list_store_append (model, &iter);
+		map = gog_theme_get_color_map (theme, FALSE);
+		pixbuf = gog_axis_color_map_get_snapshot (map, FALSE,
+		                                          TRUE, 200, 16);
+		gtk_list_store_set (model, &iter, 0, gog_axis_color_map_get_name (map),
+		                    1, pixbuf, 2, map, -1);
+		g_object_unref (pixbuf);
+		if (map == axis->color_map)
+			gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+		/* add all locally defined maps */
+		color_state.combo = GTK_COMBO_BOX (combo);
+		color_state.model = model;
+		color_state.target = axis->color_map;
+		color_state.discrete = axis->type == GOG_AXIS_PSEUDO_3D;
+		gog_axis_color_map_foreach ((GogAxisColorMapHandler) add_color_map_cb, &color_state);
+		/* add document maps if any */
+		/* TODO */
+		g_signal_connect (combo, "changed", G_CALLBACK (color_map_changed_cb), axis);
+		w = go_gtk_builder_get_widget (gui, "new-btn");
+		g_signal_connect_swapped (w, "clicked", G_CALLBACK (color_map_new_cb), combo);
+		gtk_widget_hide (w);
+		w = go_gtk_builder_get_widget (gui, "duplicate-btn");
+		gtk_widget_hide (w);
+		w = go_gtk_builder_get_widget (gui, "edit-btn");
+		gtk_widget_hide (w);
+		go_editor_add_page (editor,
+		                    go_gtk_builder_get_widget (gui, "color-map-grid"),
+		                    _("Colors"));
+	}
 
 	if (gog_object_is_visible (axis) && gog_axis_get_atype (axis) < GOG_AXIS_VIRTUAL) {
 	    /* Style page */
@@ -2912,6 +3037,11 @@ gog_axis_class_init (GObjectClass *gobject_klass)
 			_("Position of the plot area at which the axis effective area ends, expressed as a percentage of the available position. Defaults to 1.0"),
 			0., 1., 1.,
 			GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+	g_object_class_install_property (gobject_klass, AXIS_PROP_COLOR_MAP,
+		g_param_spec_string ("color-map-name", _("ColorMapName"),
+			_("The name of the color map"),
+			"default",
+			GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
 
 	gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
 
@@ -3349,7 +3479,6 @@ gog_axis_get_color_map (GogAxis *axis)
 	g_return_val_if_fail (GOG_IS_AXIS (axis), NULL);
 
 	return axis->color_map;
-	
 }
 
 /****************************************************************************/
diff --git a/goffice/graph/gog-color-scale.c b/goffice/graph/gog-color-scale.c
index a4dfd03..b1f24ed 100644
--- a/goffice/graph/gog-color-scale.c
+++ b/goffice/graph/gog-color-scale.c
@@ -29,12 +29,22 @@
 #include <gtk/gtk.h>
 #endif
 
+/**
+ * SECTION: gog-color-scale
+ * @short_description: Displays the color scale used by an axis.
+ *
+ * A color scale has two parts: an axis, and a rectangle filled by the colors
+ * corresponding to the axis scale. It can be displayed horizontally or
+ * vertically.
+ **/
 struct _GogColorScale {
 	GogStyledObject base;
 	GogAxis *color_axis; /* the color or pseudo-3d axis */
 	GogAxis *axis; /* the axis used to display the scale */
 	gboolean horizontal;
-	double width; /* will actually be height if horizontal */
+	gboolean axis_at_low;	/* axis position on low coordinates side */
+	double width; /* will actually be height of the colored rectangle if
+	 			   * horizontal */
 };
 typedef GogStyledObjectClass GogColorScaleClass;
 
diff --git a/goffice/graph/gog-graph.c b/goffice/graph/gog-graph.c
index 4f21ac9..c723e67 100644
--- a/goffice/graph/gog-graph.c
+++ b/goffice/graph/gog-graph.c
@@ -630,9 +630,7 @@ gog_graph_num_rows (GogGraph const *graph)
 GogGraph *
 gog_graph_dup (GogGraph const *graph)
 {
-	GogObject *res = gog_object_dup (GOG_OBJECT (graph), NULL, NULL);
-	GOG_GRAPH (res)->doc = graph->doc;
-	return GOG_GRAPH (res);
+	return GOG_GRAPH (gog_object_dup (GOG_OBJECT (graph), NULL, NULL));
 }
 
 /**
@@ -1289,3 +1287,17 @@ void gog_graph_render_to_cairo (GogGraph *graph, cairo_t *cairo, double w, doubl
 	gog_renderer_render_to_cairo (renderer, cairo, w, h);
 	g_object_unref (renderer);
 }
+
+/**
+ * gog_graph_get_document:
+ * @graph: a #GogGraph
+ *
+ * Retrieves the #GODoc for @graph.
+ * Returns: (transfer none): the document.
+ **/
+GODoc *
+gog_graph_get_document (GogGraph *graph)
+{
+	g_return_val_if_fail (GOG_IS_GRAPH (graph), NULL);
+	return graph->doc;
+}
diff --git a/goffice/graph/gog-graph.h b/goffice/graph/gog-graph.h
index 7714f0e..a4ba6c2 100644
--- a/goffice/graph/gog-graph.h
+++ b/goffice/graph/gog-graph.h
@@ -73,6 +73,7 @@ void  	 gog_graph_view_handle_event 	(GogGraphView *gview, GdkEvent *event, doub
 #endif
 
 void 	 gog_graph_render_to_cairo 	(GogGraph *graph, cairo_t *cairo, double w, double h);
+GODoc	*gog_graph_get_document		(GogGraph *graph);
 
 G_END_DECLS
 
diff --git a/goffice/graph/gog-object-xml.c b/goffice/graph/gog-object-xml.c
index 9a7edbd..78665b2 100644
--- a/goffice/graph/gog-object-xml.c
+++ b/goffice/graph/gog-object-xml.c
@@ -20,14 +20,7 @@
  */
 
 #include <goffice/goffice-config.h>
-#include <goffice/graph/gog-object-xml.h>
-#include <goffice/graph/gog-object.h>
-#include <goffice/graph/gog-plot.h>
-#include <goffice/graph/gog-trend-line.h>
-#include <goffice/graph/gog-data-set.h>
-#include <goffice/data/go-data.h>
-#include <goffice/utils/go-color.h>
-#include <goffice/utils/go-persist.h>
+#include <goffice/goffice.h>
 
 #include <string.h>
 #include <stdlib.h>
@@ -449,6 +442,8 @@ gogo_start (GsfXMLIn *xin, xmlChar const **attrs)
 		if (res == NULL) {
 			g_warning ("unknown type '%s'", type);
 		}
+		if (GOG_IS_GRAPH (res))
+			((GogGraph *) res)->doc = (GODoc *) g_object_get_data (G_OBJECT (gsf_xml_in_get_input (xin)), "document");
 	} else
 		res = NULL;
 
diff --git a/goffice/graph/gog-object.c b/goffice/graph/gog-object.c
index f80d53b..df7dd31 100644
--- a/goffice/graph/gog-object.c
+++ b/goffice/graph/gog-object.c
@@ -1090,7 +1090,9 @@ gog_object_dup (GogObject const *src, GogObject *new_parent, GogDataDuplicator d
 		else
 			dataset_dup (GOG_DATASET (src), GOG_DATASET (dst));
 	}
-	if (GOG_IS_CHART (src))
+	if (GOG_IS_GRAPH (src))
+		GOG_GRAPH (dst)->doc = GOG_GRAPH (src)->doc;
+	else if (GOG_IS_CHART (src))
 		GOG_CHART (dst)->axis_set = GOG_CHART (src)->axis_set;
 
 	for (ptr = src->children; ptr != NULL ; ptr = ptr->next)
diff --git a/goffice/graph/gog-theme.c b/goffice/graph/gog-theme.c
index 7f64ebb..e5996c8 100644
--- a/goffice/graph/gog-theme.c
+++ b/goffice/graph/gog-theme.c
@@ -241,6 +241,8 @@ gog_theme_finalize (GObject *obj)
 			g_object_unref (g_ptr_array_index (theme->palette, i));
 		g_ptr_array_free (theme->palette, TRUE);
 	}
+	if (theme->cm)
+		g_object_unref (theme->cm);
 	if (theme->dcm)
 		g_object_unref (theme->dcm);
 
@@ -846,7 +848,7 @@ build_predefined_themes (void)
 		NULL, "GogEquation", NULL);
 #endif
 	/* builds the default discrete color map */
-	theme->dcm = gog_axis_color_map_from_colors ("Default discrete",
+	theme->dcm = gog_axis_color_map_from_colors (N_("Theme"),
 	                                             G_N_ELEMENTS (default_palette),
 	                                             default_palette);
 
@@ -995,7 +997,7 @@ build_predefined_themes (void)
 		NULL, "GogEquation", NULL);
 #endif
 
-	theme->dcm = gog_axis_color_map_from_colors ("Default discrete",
+	theme->dcm = gog_axis_color_map_from_colors (N_("Theme"),
 	                                             G_N_ELEMENTS (guppi_palette),
 	                                             guppi_palette);
 }
@@ -1029,6 +1031,7 @@ name_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
 	if (state->lang == NULL) {
 		GOStyle *style;
 		state->theme = gog_theme_new (name);
+		name = NULL;
 		state->theme->palette = g_ptr_array_new ();
 		/* initialize a dummy GogSeries style */
 		style = go_style_new ();
@@ -1043,11 +1046,13 @@ name_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
 		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->local_name = name;
+				name = NULL;
 				state->name_lang_score = i;
 			}
 		}
 	}
+	g_free (name);
 	g_free (state->lang);
 	state->lang = NULL;
 }
@@ -1069,8 +1074,8 @@ desc_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
 			}
 		}
 	}
-	g_free (state->lang);
-	state->lang = NULL;
+	g_free (state->desc);
+	state->desc = NULL;
 }
 
 static void
@@ -1113,8 +1118,10 @@ theme_load_from_uri (char const *uri)
 	GsfXMLInDoc *xml;
 	GsfInput *input = go_file_open (uri, NULL);
 
-	if (input == NULL)
+	if (input == NULL) {
 		g_warning ("[GogTheme]: Could not open %s", uri);
+		return;
+	}
 	state.theme = NULL;
 	state.desc = state.lang = state.local_name = NULL;
 	state.langs = g_get_language_names ();
@@ -1128,7 +1135,7 @@ theme_load_from_uri (char const *uri)
 			unsigned i;
 			for (i = 0; i < state.theme->palette->len; i++)
 				colors[i] = GO_STYLE (g_ptr_array_index (state.theme->palette, i))->fill.pattern.back;
-			state.theme->dcm = gog_axis_color_map_from_colors ("Default discrete", state.theme->palette->len, colors);
+			state.theme->dcm = gog_axis_color_map_from_colors ("Default", state.theme->palette->len, colors);
 			g_free (colors);
 		}
 		state.theme->local_name = state.local_name;
@@ -1199,8 +1206,10 @@ _gog_themes_shutdown (void)
 /**
  * gog_theme_get_color_map:
  * @theme: #GogTheme
- * @discrete:
+ * @discrete: whether the map is for a discrete axis.
  *
+ * Retrieves the themed color map. Each theme has a discrete color map and a
+ * continuous one.
  * Returns: (transfer none): the requested color map.
  **/
 GogAxisColorMap const *
diff --git a/goffice/gtk/go-locale-sel.c b/goffice/gtk/go-locale-sel.c
index f8ea999..7b53223 100644
--- a/goffice/gtk/go-locale-sel.c
+++ b/goffice/gtk/go-locale-sel.c
@@ -158,7 +158,7 @@ static LocaleInfo locale_trans_array[] = {
 	{N_("Luxembourg/German (de_LU)"),                "de_LU", LG_WESTERN_EUROPE },
 	{N_("Maldives (dv_MV)"),                         "dv_MV", LG_ASIA },
 	{N_("Bhutan (dz_BT)"),                           "dz_BT", LG_ASIA },
-	{N_("Cyprus/Greek (el_CY)"),                     "el_CY", LG_WESTERN_EUROPE },  
+	{N_("Cyprus/Greek (el_CY)"),                     "el_CY", LG_WESTERN_EUROPE },
 	{N_("Greece (el_GR)"),                           "el_GR", LG_WESTERN_EUROPE },
 	{N_("Antigua and Barbuda/English (en_AG)"),      "en_AG", LG_CARIBBEAN },
 	{N_("Australia (en_AU)"),                        "en_AU", LG_AUSTRALIA },
diff --git a/goffice/utils/go-path.c b/goffice/utils/go-path.c
index cdc3b6b..0057735 100644
--- a/goffice/utils/go-path.c
+++ b/goffice/utils/go-path.c
@@ -896,7 +896,7 @@ go_path_to_svg (GOPath *path)
 	                   (GOPathLineToFunc) go_path_svg_line_to,
 	                   (GOPathCurveToFunc) go_path_svg_curve_to,
 	                   (GOPathClosePathFunc) go_path_svg_close, &closure);
-	
+
 	return g_string_free (closure.str, FALSE);
 }
 
diff --git a/plugins/plot_surface/gog-contour.c b/plugins/plot_surface/gog-contour.c
index c42c514..a406c12 100644
--- a/plugins/plot_surface/gog-contour.c
+++ b/plugins/plot_surface/gog-contour.c
@@ -142,7 +142,7 @@ gog_contour_plot_foreach_elem  (GogPlot *plot, gboolean only_visible,
 	GogAxisColorMap const *map = gog_axis_get_color_map (axis);
 	GogAxisTick *zticks;
 	double *limits;
-	double minimum, maximum, epsilon;
+	double minimum, maximum, epsilon, scale;
 	char const *separator = go_locale_get_decimal ()->str;
 
 	/* First get the series name and style */
@@ -170,6 +170,7 @@ gog_contour_plot_foreach_elem  (GogPlot *plot, gboolean only_visible,
 		limits[j] = maximum;
 	else
 		j--;
+	scale = (j > gog_axis_color_map_get_max (map) && j > 1)? (double) gog_axis_color_map_get_max (map) / (j - 1): 1.;
 
 	style->interesting_fields = GO_STYLE_FILL | GO_STYLE_OUTLINE;
 	style->fill.type = GO_STYLE_FILL_PATTERN;
@@ -177,14 +178,14 @@ gog_contour_plot_foreach_elem  (GogPlot *plot, gboolean only_visible,
 
 	if (gog_axis_is_inverted (axis)) {
 		for (i = 0; i < j; i++) {
-			style->fill.pattern.back = (j < 2)? GO_COLOR_WHITE: gog_axis_color_map_get_color (map, i);
+			style->fill.pattern.back = (j < 2)? GO_COLOR_WHITE: gog_axis_color_map_get_color (map, i * scale);
 			label = g_strdup_printf ("[%g%s %g%c", limits[j - i - 1], separator,
 						limits[j - i], (limits[i - j] - minimum > epsilon)? '[':']');
 			(func) (i, style, label, NULL, data);
 			g_free (label);
 		}
 		if (limits[i - j] - minimum > epsilon) {
-			style->fill.pattern.back = (j < 2)? GO_COLOR_WHITE: gog_axis_color_map_get_color (map, i);
+			style->fill.pattern.back = (j < 2)? GO_COLOR_WHITE: gog_axis_color_map_get_color (map, i * scale);
 			label = g_strdup_printf ("[%g%s %g]", minimum, separator,
 						limits[i - j]);
 			(func) (i + 1, style, label, NULL, data);
@@ -202,7 +203,7 @@ gog_contour_plot_foreach_elem  (GogPlot *plot, gboolean only_visible,
 		} else
 			i = 0;
 		for (; i < j; i++) {
-			style->fill.pattern.back = (j < 2)? GO_COLOR_WHITE: gog_axis_color_map_get_color (map, i);
+			style->fill.pattern.back = (j < 2)? GO_COLOR_WHITE: gog_axis_color_map_get_color (map, i * scale);
 			label = g_strdup_printf ("[%g%s %g%c", limits[i], separator,
 						limits[i + 1], (i == j - 1)? ']':'[');
 			(func) (i + 1, style, label, NULL, data);
@@ -337,8 +338,11 @@ gog_contour_view_render (GogView *view, GogViewAllocation const *bbox)
 	color = g_new0 (GOColor, max);
 	if (max < 2)
 		color[0] = GO_COLOR_WHITE;
-	else for (i = 0; i < (unsigned) max; i++)
-		color[i] = gog_axis_color_map_get_color (color_map, i);
+	else {
+		double scale = ((unsigned) max > gog_axis_color_map_get_max (color_map))? (double) gog_axis_color_map_get_max (color_map) / (max - 1): 1.;
+		for (i = 0; i < (unsigned) max; i++)
+			color[i] = gog_axis_color_map_get_color (color_map, i * scale);
+	}
 
 	/* clip to avoid problems with logarithmic axes */
 	gog_renderer_push_clip_rectangle (rend, view->residual.x, view->residual.y,
diff --git a/po/POTFILES.in b/po/POTFILES.in
index bfdcdeb..b153977 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -71,6 +71,8 @@ goffice/graph/gog-axis-line.c
 [type: gettext/glade]goffice/graph/gog-axis-prefs.ui
 goffice/graph/gog-axis.c
 goffice/graph/gog-axis.h
+goffice/graph/gog-axis-color-map.c
+[type: gettext/glade]goffice/graph/gog-axis-color-map-prefs.ui
 goffice/graph/gog-chart-impl.h
 goffice/graph/gog-chart.c
 goffice/graph/gog-chart.h



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]