[goffice] Implement color map duplication. Fix #657908



commit 0006a32bbdf88116cae209cdcc550ab658462467
Author: Jean Brefort <jean brefort normalesup org>
Date:   Mon Nov 5 14:57:54 2012 +0100

    Implement color map duplication. Fix #657908

 ChangeLog                                 |   31 ++
 NEWS                                      |    1 +
 docs/reference/goffice-0.10-sections.txt  |    9 +-
 goffice/app/go-doc.c                      |   34 +-
 goffice/app/go-doc.h                      |    2 +-
 goffice/graph/gog-axis-color-map-prefs.ui |    4 +-
 goffice/graph/gog-axis-color-map.c        |  650 +++++++++++++++++------------
 goffice/graph/gog-axis-color-map.h        |    8 +-
 goffice/graph/gog-axis-prefs.ui           |    7 +-
 goffice/graph/gog-axis.c                  |   61 ++-
 goffice/graph/gog-theme.c                 |    9 +-
 goffice/utils/go-persist.c                |    2 +-
 goffice/utils/goffice-utils.c             |   28 ++
 goffice/utils/goffice-utils.h             |   10 +
 14 files changed, 550 insertions(+), 306 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 29afced..e98eded 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2012-11-05  Jean Brefort  <jean brefort normalesup org>
+
+	* goffice/app/go-doc.c (go_doc_write), (load_color_map),
+	(go_doc_save_resource): simplify.
+	* goffice/app/go-doc.h: ditto.
+	* goffice/graph/gog-axis-color-map-prefs.ui: only save a modified map.
+	* goffice/graph/gog-axis-color-map.c
+	(gog_axis_color_map_set_property),
+	(gog_axis_color_map_get_property), (gog_axis_color_map_class_init),
+	(save_name_cb), (build_uri), (gog_axis_color_map_write),
+	(color_stop_start), (map_start), (name_start), (name_end),
+	(color_stops_cmp), (gog_axis_color_map_save), (color_map_loaded),
+	(parse_done_cb), (gog_axis_color_map_prep_sax),
+	(gog_axis_map_persist_init),
+	(gog_axis_color_map_get_resource_type),
+	(gog_axis_color_map_set_name), (gog_axis_color_map_dup),
+	(update_snapshots), (gog_axis_color_map_edit),
+	(gog_axis_color_map_from_colors), (gog_axis_color_map_get_from_id),
+	(_gog_axis_color_maps_init), (_gog_axis_color_maps_shutdown): implement
+	color map duplication.
+	* goffice/graph/gog-axis-color-map.h: ditto.
+	* goffice/graph/gog-axis-prefs.ui: ditto.
+	* goffice/graph/gog-axis.c (gog_axis_sax_save): save a color map loaded
+	from a document, (color_map_dup_cb), (color_map_save_cb),
+	(color_map_changed_cb), (gog_axis_populate_editor): color map duplication.
+	* goffice/graph/gog-theme.c (build_predefined_themes),
+	(theme_load_from_uri): ditto.
+	* goffice/utils/go-persist.c: fix includes.
+	* goffice/utils/goffice-utils.c (go_resource_type_get_type): add new enum.
+	* goffice/utils/goffice-utils.h: ditto.
+
 2012-11-04  Jean Brefort  <jean brefort normalesup org>
 
 	* goffice/graph/gog-axis-color-map-prefs.ui: make it functional.
diff --git a/NEWS b/NEWS
index 7d6180a..a224df7 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,7 @@ Jean:
 	* Fixed image clipping. [#686490]
 	* Improve padding between axis ticks and labels.  [#686473]
 	* Enhance graph guru behavior when an object is deleted. [#687102]
+	* Allow colors selection for contour and map plots. [#657908]
 
 Morten:
 	* Namespace cleanup.  [#686501]
diff --git a/docs/reference/goffice-0.10-sections.txt b/docs/reference/goffice-0.10-sections.txt
index e0dd922..062d7a9 100644
--- a/docs/reference/goffice-0.10-sections.txt
+++ b/docs/reference/goffice-0.10-sections.txt
@@ -854,8 +854,8 @@ go_doc_init_write
 go_doc_is_dirty
 go_doc_is_pristine
 go_doc_read
-go_doc_save_color_map
 go_doc_save_image
+go_doc_save_resource
 go_doc_set_dirty
 go_doc_set_dirty_time
 go_doc_set_meta_data
@@ -2054,6 +2054,9 @@ GOLineDashType
 GOLineInterpolation
 GOTranslateFunc
 GOFontScript
+GoResourceType
+<SUBSECTION Standard>
+go_resource_type_get_type
 </SECTION>
 
 <SECTION>
@@ -2744,6 +2747,7 @@ gog_axis_base_view_get_type
 GogAxisColorMap
 gog_axis_color_map_edit
 GogAxisColorMapHandler
+gog_axis_color_map_dup
 gog_axis_color_map_foreach
 gog_axis_color_map_from_colors
 gog_axis_color_map_get_color
@@ -2751,9 +2755,8 @@ gog_axis_color_map_get_from_id
 gog_axis_color_map_get_max
 gog_axis_color_map_get_id
 gog_axis_color_map_get_name
+gog_axis_color_map_get_resource_type
 gog_axis_color_map_get_snapshot
-gog_axis_color_map_sax_push_parser
-gog_axis_color_map_write
 <SUBSECTION Standard>
 GOG_AXIS_COLOR_MAP
 GOG_IS_AXIS_COLOR_MAP
diff --git a/goffice/app/go-doc.c b/goffice/app/go-doc.c
index 971800f..a8f30ab 100644
--- a/goffice/app/go-doc.c
+++ b/goffice/app/go-doc.c
@@ -35,7 +35,7 @@ struct _GODocPrivate {
 	GHashTable	*imagebuf; /* used when loading/saving images */
 
 	/* color maps */
-	GSList	*colormapsbuf; /* used when loading/saving color maps */
+	GSList	*resourcesbuf; /* used when loading/saving color maps and themes */
 };
 
 /**
@@ -506,13 +506,13 @@ go_doc_write (GODoc *doc, GsfXMLOut *output)
 {
 	GSList *ptr;
 	if (g_hash_table_size (doc->priv->imagebuf) > 0 ||
-	    doc->priv->colormapsbuf != NULL) {
+	    doc->priv->resourcesbuf != NULL) {
 		gsf_xml_out_start_element (output, "GODoc");
 		g_hash_table_foreach (doc->priv->imagebuf, save_image_cb, output);
-		for (ptr = doc->priv->colormapsbuf; ptr; ptr = ptr->next)
-			gog_axis_color_map_write (GOG_AXIS_COLOR_MAP (ptr->data), output);
-		g_slist_free (doc->priv->colormapsbuf);
-		doc->priv->colormapsbuf = NULL;
+		for (ptr = doc->priv->resourcesbuf; ptr; ptr = ptr->next)
+			go_persist_sax_save (GO_PERSIST (ptr->data), output);
+		g_slist_free (doc->priv->resourcesbuf);
+		doc->priv->resourcesbuf = NULL;
 		gsf_xml_out_end_element (output);
 	}
 	g_hash_table_destroy (doc->priv->imagebuf);
@@ -593,7 +593,13 @@ load_image_data (GsfXMLIn *xin, GsfXMLBlob *unknown)
 static void
 load_color_map (GsfXMLIn *xin, xmlChar const **attrs)
 {
-	gog_axis_color_map_sax_push_parser (xin, attrs);
+	GogAxisColorMap *map = NULL;
+	for (; attrs && *attrs; attrs +=2)
+		if (!strcmp ((char const *) *attrs, "id")) {
+			map = GOG_AXIS_COLOR_MAP (gog_axis_color_map_get_from_id ((char const *) attrs[1]));
+			break;
+		}
+	go_persist_prep_sax (GO_PERSIST (map), xin, attrs);
 }
 
 void
@@ -656,19 +662,19 @@ go_doc_image_fetch (GODoc *doc, char const *id, GType type)
 }
 
 /**
- * go_doc_save_color_map:
+ * go_doc_save_resource:
  * @doc: a #GODoc
- * @map: the #GogAxisColorMap to save
+ * @gp: the #GOPersist to save
  *
- * Saves the color map with the document. Each color map will be saved only
+ * Saves the resource with the document. Each resource will be saved only
  * once.
  **/
 void
-go_doc_save_color_map (GODoc *doc, GogAxisColorMap	const *map)
+go_doc_save_resource (GODoc *doc, GOPersist const *gp)
 {
 	GSList *ptr;
-	for (ptr = doc->priv->colormapsbuf; ptr; ptr = ptr->next)
-		if (ptr->data == map) /* already marked for saving */
+	for (ptr = doc->priv->resourcesbuf; ptr; ptr = ptr->next)
+		if (ptr->data == gp) /* already marked for saving */
 			return;
-	doc->priv->colormapsbuf = g_slist_prepend (doc->priv->colormapsbuf, (void *) map);
+	doc->priv->resourcesbuf = g_slist_prepend (doc->priv->resourcesbuf, (void *) gp);
 }
diff --git a/goffice/app/go-doc.h b/goffice/app/go-doc.h
index 82be494..4410227 100644
--- a/goffice/app/go-doc.h
+++ b/goffice/app/go-doc.h
@@ -61,7 +61,7 @@ void		 go_doc_init_read	(GODoc *doc, GsfInput *input);
 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);
-void		 go_doc_save_color_map (GODoc *doc, GogAxisColorMap	const *map);
+void		 go_doc_save_resource (GODoc *doc, GOPersist const *gp);
 
 G_END_DECLS
 
diff --git a/goffice/graph/gog-axis-color-map-prefs.ui b/goffice/graph/gog-axis-color-map-prefs.ui
index 40a4f1d..1050506 100644
--- a/goffice/graph/gog-axis-color-map-prefs.ui
+++ b/goffice/graph/gog-axis-color-map-prefs.ui
@@ -36,7 +36,7 @@
               </packing>
             </child>
             <child>
-              <object class="GtkButton" id="button1">
+              <object class="GtkButton" id="save">
                 <property name="label">gtk-save</property>
                 <property name="use_action_appearance">False</property>
                 <property name="visible">True</property>
@@ -288,7 +288,7 @@
     </child>
     <action-widgets>
       <action-widget response="0">button2</action-widget>
-      <action-widget response="1">button1</action-widget>
+      <action-widget response="1">save</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 7ed0414..b732c0c 100644
--- a/goffice/graph/gog-axis-color-map.c
+++ b/goffice/graph/gog-axis-color-map.c
@@ -50,6 +50,7 @@ struct _GogAxisColorMap {
 	char *id, *name;
 	char *uri;
 	GHashTable *names;
+	GoResourceType type;
 	unsigned size; /* colors number */
 	unsigned allocated; /* only useful when editing */
 	unsigned *limits;
@@ -59,6 +60,44 @@ typedef GObjectClass GogAxisColorMapClass;
 
 static GObjectClass *parent_klass;
 
+enum {
+	GOG_AXIS_COLOR_MAP_PROP_0,
+	GOG_AXIS_COLOR_MAP_PROP_TYPE
+};
+
+static void
+gog_axis_color_map_set_property (GObject *gobject, guint param_id,
+		       GValue const *value, GParamSpec *pspec)
+{
+	GogAxisColorMap *map = GOG_AXIS_COLOR_MAP (gobject);
+
+	switch (param_id) {
+	case GOG_AXIS_COLOR_MAP_PROP_TYPE:
+		map->type = g_value_get_enum (value);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		return; /* NOTE : RETURN */
+	}
+}
+
+static void
+gog_axis_color_map_get_property (GObject *gobject, guint param_id,
+		       GValue *value, GParamSpec *pspec)
+{
+	GogAxisColorMap *map = GOG_AXIS_COLOR_MAP (gobject);
+
+	switch (param_id) {
+	case GOG_AXIS_COLOR_MAP_PROP_TYPE:
+		g_value_set_enum (value, map->type);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		return; /* NOTE : RETURN */
+	}
+}
 static void
 gog_axis_color_map_finalize (GObject *obj)
 {
@@ -84,6 +123,14 @@ gog_axis_color_map_class_init (GObjectClass *gobject_klass)
 	parent_klass = g_type_class_peek_parent (gobject_klass);
 	/* GObjectClass */
 	gobject_klass->finalize = gog_axis_color_map_finalize;
+	gobject_klass->set_property = gog_axis_color_map_set_property;
+	gobject_klass->get_property = gog_axis_color_map_get_property;
+	g_object_class_install_property (gobject_klass, GOG_AXIS_COLOR_MAP_PROP_TYPE,
+		g_param_spec_enum ("resource-type",
+			_("Resource type"),
+			_("The resource type for the color map"),
+			go_resource_type_get_type (), GO_RESOURCE_INVALID,
+		        GSF_PARAM_STATIC | G_PARAM_READWRITE |G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
@@ -92,9 +139,259 @@ gog_axis_color_map_init (GogAxisColorMap *map)
 	map->names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 }
 
-GSF_CLASS (GogAxisColorMap, gog_axis_color_map,
-	   gog_axis_color_map_class_init, gog_axis_color_map_init,
-	   G_TYPE_OBJECT)
+static void
+save_name_cb (char const *lang, char const *name, GsfXMLOut *output)
+{
+	gsf_xml_out_start_element (output, "name");
+	if (strcmp (lang, "C"))
+		gsf_xml_out_add_cstr_unchecked (output, "xml:lang", lang);
+	gsf_xml_out_add_cstr_unchecked (output, NULL, name);
+	gsf_xml_out_end_element (output);
+}
+
+static void gog_axis_color_map_save (GogAxisColorMap const *map);
+
+static void
+build_uri (GogAxisColorMap *map)
+{
+	char *filename, *full_name;
+	filename = g_strconcat (map->id, ".map", NULL);
+	full_name = g_build_filename (g_get_home_dir (), ".goffice", "colormaps", filename, NULL);
+	map->uri = go_filename_to_uri (full_name);
+	g_free (filename);
+	g_free (full_name);
+}
+
+static void
+gog_axis_color_map_write (GOPersist const *gp, GsfXMLOut *output)
+{
+	unsigned i;
+	char *buf;
+	GogAxisColorMap *map;
+	g_return_if_fail (GOG_IS_AXIS_COLOR_MAP (gp));
+
+	map = GOG_AXIS_COLOR_MAP (gp);
+	if (output == NULL) {
+		g_return_if_fail (map->uri == NULL);
+		build_uri (map);
+		map->type = GO_RESOURCE_RW;
+		gog_axis_color_map_save (map);
+		return;
+	}
+	gsf_xml_out_start_element (output, "GogAxisColorMap");
+	gsf_xml_out_add_cstr_unchecked (output, "id", map->id);
+	g_hash_table_foreach (map->names, (GHFunc) save_name_cb, output);
+	for (i = 0; i < map->size; i++) {
+		gsf_xml_out_start_element (output, "color-stop");
+		gsf_xml_out_add_uint (output, "bin", map->limits[i]);
+		buf = go_color_as_str (map->colors[i]);
+		gsf_xml_out_add_cstr_unchecked (output, "color", buf);
+		g_free (buf);
+		gsf_xml_out_end_element (output);
+	}
+	gsf_xml_out_end_element (output);
+}
+
+struct _color_stop {
+	unsigned bin;
+	GOColor color;
+};
+
+struct color_map_load_state {
+	GogAxisColorMap *map;
+	char *lang, *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;
+
+	if (state->map->name)
+		return;
+	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
+map_start (GsfXMLIn *xin, xmlChar const **attrs)
+{
+	struct color_map_load_state	*state = (struct color_map_load_state *) xin->user_state;
+	if (state->map ==  NULL) {
+		state->map = g_object_new (GOG_TYPE_AXIS_COLOR_MAP, NULL);
+		for (; attrs && *attrs; attrs +=2)
+			if (!strcmp ((char const *) *attrs, "id")) {
+				state->map->id = g_strdup ((char const *) attrs[1]);
+				break;
+			}
+	}
+}
+
+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;
+	if (state->map->name)
+		return;
+	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 (state->map->name)
+		return;
+	if (xin->content->str == NULL)
+		return;
+	name = g_strdup (xin->content->str);
+	if (state->lang == NULL)
+	state->lang = g_strdup ("C");
+	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->name);
+				state->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 GsfXMLInNode const color_map_dtd[] = {
+	GSF_XML_IN_NODE (THEME, THEME, -1, "GogAxisColorMap", GSF_XML_NO_CONTENT, map_start, 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
+};
+static GsfXMLInDoc *xml = NULL;
+
+static void
+gog_axis_color_map_save (GogAxisColorMap const *map)
+{
+	GsfOutput *output = gsf_output_gio_new_for_uri (map->uri, NULL);
+	GsfXMLOut *xml = gsf_xml_out_new (output);
+	gog_axis_color_map_write (GO_PERSIST (map), xml);
+	g_object_unref (xml);
+	g_object_unref (output);
+}
+
+static void
+color_map_loaded (struct color_map_load_state *state, char const *uri, gboolean delete_invalid)
+{
+	GSList *ptr;
+	if (!state->map || state->map->name)
+		return;
+	state->map->name = state->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);
+		if (delete_invalid) {
+			g_object_unref (state->map);
+			state->map = NULL;
+		}
+	} else {
+		unsigned cur_bin, n = 0;
+		state->map->allocated = g_slist_length (state->color_stops);
+		state->map->limits = g_new (unsigned, state->map->allocated);
+		state->map->colors = g_new (GOColor, state->map->allocated);
+		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 */
+		if (state->map->id == NULL) {
+			if (state->map->uri) {
+				state->map->id = go_uuid ();
+				gog_axis_color_map_save (state->map);
+			} else {
+				g_warning ("[GogAxisColorMap]: Map without Id in %s", uri);
+				if (delete_invalid) {
+					g_object_unref (state->map);
+					state->map = NULL;
+				}
+			}
+		}
+	}
+	g_slist_free_full (state->color_stops, g_free);
+	g_free (state->lang);
+}
+
+static void
+parse_done_cb (GsfXMLIn *xin, struct color_map_load_state *state)
+{
+	color_map_loaded (state, gsf_input_name (gsf_xml_in_get_input (xin)), FALSE);
+	g_free (state);
+}
+
+static void
+gog_axis_color_map_prep_sax (GOPersist *gp, GsfXMLIn *xin, xmlChar const **attrs)
+{
+	struct color_map_load_state *state;
+
+	g_return_if_fail (GOG_IS_AXIS_COLOR_MAP (gp));
+
+	state = g_new (struct color_map_load_state, 1);
+	state->map = GOG_AXIS_COLOR_MAP (gp);
+	state->name = NULL;
+	state->lang = NULL;
+	state->langs = g_get_language_names ();
+	state->name_lang_score = G_MAXINT;
+	state->color_stops = NULL;
+	if (!xml)
+		xml = gsf_xml_in_doc_new (color_map_dtd, NULL);
+	gsf_xml_in_push_state (xin, xml, state, (GsfXMLInExtDtor) parse_done_cb, attrs);
+}
+
+static void
+gog_axis_map_persist_init (GOPersistClass *iface)
+{
+	iface->prep_sax = gog_axis_color_map_prep_sax;
+	iface->sax_save = gog_axis_color_map_write;
+}
+
+GSF_CLASS_FULL (GogAxisColorMap, gog_axis_color_map,
+                NULL, NULL, gog_axis_color_map_class_init, NULL,
+                gog_axis_color_map_init, G_TYPE_OBJECT, 0,
+                GSF_INTERFACE (gog_axis_map_persist_init, GO_TYPE_PERSIST))
 
 /**
  * gog_axis_color_map_get_color:
@@ -166,6 +463,20 @@ gog_axis_color_map_get_name (GogAxisColorMap const *map)
 }
 
 /**
+ * gog_axis_color_map_get_resource_type:
+ * @map: a #GogAxisMap
+ *
+ * Retrieves the resource type for @map.
+ * Returns: the resource type.
+ **/
+GoResourceType
+gog_axis_color_map_get_resource_type (GogAxisColorMap const *map)
+{
+	g_return_val_if_fail (GOG_IS_AXIS_COLOR_MAP (map), GO_RESOURCE_INVALID);
+	return map->type;
+}
+
+/**
  * gog_axis_color_map_get_snapshot:
  * @map: a #GogAxisMap
  * @discrete: whether to use constant colors between each stop or a gradient.
@@ -252,13 +563,39 @@ gog_axis_color_map_get_snapshot (GogAxisColorMap const *map,
 }
 
 static void
-gog_axis_color_map_save (GogAxisColorMap const *map)
+gog_axis_color_map_set_name (GogAxisColorMap *map, char const *name)
 {
-	GsfOutput *output = gsf_output_gio_new_for_uri (map->uri, NULL);
-	GsfXMLOut *xml = gsf_xml_out_new (output);
-	gog_axis_color_map_write (map, xml);
-	g_object_unref (xml);
-	g_object_unref (output);
+	g_free (map->name);
+	g_hash_table_remove_all (map->names);
+	map->name = g_strdup (name);
+	g_hash_table_insert (map->names, g_strdup ("C"), g_strdup (name));
+}
+
+/**
+ * gog_axis_color_map_dup:
+ * @map: a #GogAxisColorMap
+ *
+ * Duplicates the color map.
+ * Returns: (transfer none): the new color map/
+ **/
+GogAxisColorMap *
+gog_axis_color_map_dup (GogAxisColorMap const *map)
+{
+	unsigned i;
+	GogAxisColorMap *new_map = g_object_new (GOG_TYPE_AXIS_COLOR_MAP,
+	                                         "resource-type", GO_RESOURCE_RW,
+	                                         NULL);
+	gog_axis_color_map_set_name (new_map, _("New map"));
+	new_map->id = go_uuid ();
+	build_uri (new_map);
+	new_map->size = new_map->allocated = map->size;
+	new_map->limits = g_new (unsigned, map->size);
+	new_map->colors = g_new (GOColor, map->size);
+	for (i = 0; i < map->size; i++) {
+		new_map->limits[i] = map->limits[i];
+		new_map->colors[i] = map->colors[i];
+	}
+	return new_map;
 }
 
 static GSList *color_maps;
@@ -305,7 +642,7 @@ gog_axis_color_map_compare (GogAxisColorMap const *map1, GogAxisColorMap const *
 struct color_map_edit_state {
 	GtkWidget *color_selector, *discrete, *continuous;
 	GtkBuilder *gui;
-	GogAxisColorMap *map;
+	GogAxisColorMap *map, *orig;
 	unsigned bin;
 };
 
@@ -320,6 +657,8 @@ update_snapshots (struct color_map_edit_state *state)
 	pixbuf = gog_axis_color_map_get_snapshot (state->map, FALSE, TRUE, 200, 24);
 	gtk_image_set_from_pixbuf (GTK_IMAGE (state->continuous), pixbuf);
 	g_object_unref (pixbuf);
+	gtk_widget_set_sensitive (go_gtk_builder_get_widget (state->gui, "save"),
+	                          gog_axis_color_map_compare (state->map, state->orig));
 }
 
 static void
@@ -362,7 +701,7 @@ define_cb (struct color_map_edit_state *state)
 			         sizeof (unsigned) * (state->map->size - i - 1));
 			memmove (state->map->colors + i + 1, state->map->colors + i,
 			         sizeof (GOColor) * (state->map->size - i - 1));
-		}	
+		}
 		state->map->limits[i] = state->bin;
 		state->map->colors[i] = go_color_selector_get_color (GO_SELECTOR (state->color_selector), NULL);
 		gtk_widget_set_sensitive (go_gtk_builder_get_widget (state->gui, "erase"),
@@ -406,24 +745,21 @@ 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"), *w;
 	unsigned response;
-	gboolean created = FALSE;
 	GdkPixbuf *pixbuf;
 	GtkGrid *grid = GTK_GRID (gtk_builder_get_object (gui, "grid"));
 	struct color_map_edit_state state;
 
 	if (map == NULL) {
 		GOColor color = 0; /* full transparent */
-		char *filename, *full_name;
-		state.map = gog_axis_color_map_from_colors ("New map", 1, &color);
+		state.map = gog_axis_color_map_from_colors ("New map", 1, &color, GO_RESOURCE_RW);
 		state.map->id = go_uuid ();
-		filename = g_strconcat (state.map->id, ".map", NULL);
-		full_name = g_build_filename (g_get_home_dir (), ".goffice", "colormaps", filename, NULL);
-		state.map->uri = go_filename_to_uri (full_name);
-		g_free (filename);
-		g_free (full_name);
-		created = TRUE;
-	} else
-		state.map = gog_axis_color_map_from_colors (map->name, map->size, map->colors);
+		build_uri (state.map);
+		state.orig = gog_axis_color_map_dup (state.map);
+	} else {
+		state.orig = map;
+		state.map = gog_axis_color_map_from_colors (map->name, map->size,
+		                                            map->colors, map->type);
+	}
 
 	state.gui = gui;
 	state.bin = 0;
@@ -441,6 +777,7 @@ gog_axis_color_map_edit (GogAxisColorMap *map, GOCmdContext *cc)
 	state.color_selector = go_selector_new_color (state.map->colors[0], state.map->colors[0], "fill-color");
 	gtk_grid_attach (grid, state.color_selector, 3, 2, 1, 1);
 	gtk_entry_set_text (GTK_ENTRY (gtk_builder_get_object (gui, "name")), state.map->name);
+	gtk_widget_set_sensitive (go_gtk_builder_get_widget (gui, "save"), FALSE);
 	gtk_widget_show_all (GTK_WIDGET (grid));
 
 	/* set some signals */
@@ -450,19 +787,31 @@ gog_axis_color_map_edit (GogAxisColorMap *map, GOCmdContext *cc)
 	g_signal_connect (gtk_builder_get_object (gui, "stop-btn"), "value-changed",
 	                  G_CALLBACK (bin_changed_cb), &state);
 	response = gtk_dialog_run (GTK_DIALOG (top_level));
+	if (map == NULL)
+		g_object_unref (state.orig);
 	if (response == 1) {
-		g_free (state.map->name);
-		state.map->name = g_strdup (gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (gui, "name"))));
-		if (!map) {
+		if (!map)
 			map = state.map;
-			gog_axis_color_map_save (map);
-			gog_axis_color_map_registry_add (map);
-		} else if (gog_axis_color_map_compare (map, state.map)) {
-			g_free (map->id);
-			map->id = go_uuid ();
+		else {
+			/* copy the colors to map */
+			unsigned i;
+			map->size = state.map->size;
+			if (map->size > map->allocated) {
+				map->limits = g_new (unsigned, map->size);
+				map->colors = g_new (GOColor, map->size);
+				map->allocated = map->size;
+			}
+			for (i = 0; i < map->size; i++) {
+				map->limits[i] = state.map->limits[i];
+				map->colors[i] = state.map->colors[i];
+			}
+			g_object_unref (state.map);
 		}
+		gog_axis_color_map_set_name (map, gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (gui, "name"))));
+		gog_axis_color_map_save (map);
+		gog_axis_color_map_registry_add (map);
 	} else {
-		if (created)
+		if (map == NULL)
 			g_object_unref (state.map);
 		map = NULL;
 	}
@@ -476,18 +825,21 @@ gog_axis_color_map_edit (GogAxisColorMap *map, GOCmdContext *cc)
  * gog_axis_color_map_from_colors:
  * @name: color map name
  * @nb: colors number
- * @colors: the colors.
+ * @colors: the colors
+ * @type: the resource type
  *
  * Creates a color map using @colors.
  * Returns: (transfer full): the newly created color map.
  **/
 GogAxisColorMap *
-gog_axis_color_map_from_colors (char const *name, unsigned nb, GOColor const *colors)
+gog_axis_color_map_from_colors (char const *name, unsigned nb,
+                                GOColor const *colors, GoResourceType type)
 {
 	unsigned i;
 	GogAxisColorMap *color_map = g_object_new (GOG_TYPE_AXIS_COLOR_MAP, NULL);
 	color_map->id = g_strdup (name);
-	color_map->name = g_strdup (name);
+	gog_axis_color_map_set_name (color_map, name);
+	color_map->type = type;
 	color_map->size = color_map->allocated = nb;
 	color_map->limits = g_new (unsigned, nb);
 	color_map->colors = g_new (GOColor, nb);
@@ -507,197 +859,6 @@ _gog_axis_color_map_get_default ()
 }
 
 static void
-save_name_cb (char const *lang, char const *name, GsfXMLOut *output)
-{
-	gsf_xml_out_start_element (output, "name");
-	if (strcmp (lang, "C"))
-		gsf_xml_out_add_cstr_unchecked (output, "xml:lang", lang);
-	gsf_xml_out_add_cstr_unchecked (output, NULL, name);
-	gsf_xml_out_end_element (output);
-}
-
-/**
- * gog_axis_color_map_write:
- * @map: a #GogAxisColorMap
- * @output: a #GsfXMLOut
- *
- * Writes the color map as an XML node to @output.
- **/
-void
-gog_axis_color_map_write (GogAxisColorMap const *map, GsfXMLOut *output)
-{
-	unsigned i;
-	char *buf;
-	g_return_if_fail (GOG_IS_AXIS_COLOR_MAP (map));
-
-	gsf_xml_out_start_element (output, "GogAxisColorMap");
-	gsf_xml_out_add_cstr_unchecked (output, "id", map->id);
-	g_hash_table_foreach (map->names, (GHFunc) save_name_cb, output);
-	for (i = 0; i < map->size; i++) {
-		gsf_xml_out_start_element (output, "color-stop");
-		gsf_xml_out_add_uint (output, "bin", map->limits[i]);
-		buf = go_color_as_str (map->colors[i]);
-		gsf_xml_out_add_cstr_unchecked (output, "color", buf);
-		g_free (buf);
-		gsf_xml_out_end_element (output);
-	}
-	gsf_xml_out_end_element (output);
-}
-
-struct _color_stop {
-	unsigned bin;
-	GOColor color;
-};
-
-struct color_map_load_state {
-	GogAxisColorMap *map;
-	char *lang, *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;
-
-	if (state->map->name)
-		return;
-	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
-map_start (GsfXMLIn *xin, xmlChar const **attrs)
-{
-	struct color_map_load_state	*state = (struct color_map_load_state *) xin->user_state;
-	if (state->map ==  NULL) {
-		state->map = g_object_new (GOG_TYPE_AXIS_COLOR_MAP, NULL);
-		for (; attrs && *attrs; attrs +=2)
-			if (!strcmp ((char const *) *attrs, "id")) {
-				state->map->id = g_strdup ((char const *) attrs[1]);
-				break;
-			}
-	}
-}
-
-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;
-	if (state->map->name)
-		return;
-	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 (state->map->name)
-		return;
-	if (xin->content->str == NULL)
-		return;
-	name = g_strdup (xin->content->str);
-	if (state->lang == NULL)
-	state->lang = g_strdup ("C");
-	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->name);
-				state->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 GsfXMLInNode const color_map_dtd[] = {
-	GSF_XML_IN_NODE (THEME, THEME, -1, "GogAxisColorMap", GSF_XML_NO_CONTENT, map_start, 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
-};
-static GsfXMLInDoc *xml = NULL;
-
-static void
-color_map_loaded (struct color_map_load_state *state, char const *uri, gboolean delete_invalid)
-{
-	GSList *ptr;
-	if (!state->map || state->map->name)
-		return;
-	state->map->name = state->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);
-		if (delete_invalid) {
-			g_object_unref (state->map);
-			state->map = NULL;
-		}
-	} else {
-		unsigned cur_bin, n = 0;
-		state->map->allocated = g_slist_length (state->color_stops);
-		state->map->limits = g_new (unsigned, state->map->allocated);
-		state->map->colors = g_new (GOColor, state->map->allocated);
-		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 */
-		if (state->map->id == NULL) {
-			if (state->map->uri) {
-				state->map->id = go_uuid ();
-				gog_axis_color_map_save (state->map);
-			} else {
-				g_warning ("[GogAxisColorMap]: Map without Id in %s", uri);
-				if (delete_invalid) {
-					g_object_unref (state->map);
-					state->map = NULL;
-				}
-			}
-		}
-	}
-	g_slist_free_full (state->color_stops, g_free);
-	g_free (state->lang);
-}
-
-static void
 color_map_load_from_uri (char const *uri)
 {
 	struct color_map_load_state state;
@@ -729,40 +890,6 @@ color_map_load_from_uri (char const *uri)
 }
 
 static void
-parse_done_cb (GsfXMLIn *xin, struct color_map_load_state *state)
-{
-	color_map_loaded (state, gsf_input_name (gsf_xml_in_get_input (xin)), FALSE);
-	g_free (state);
-}
-
-/**
- * gog_axis_color_map_sax_push_parser:
- * @xin: a #GsfXMLIn
- * @attrs: the node attributes.
- *
- * Reads a colormap from the XML stream.
- **/
-void
-gog_axis_color_map_sax_push_parser (GsfXMLIn *xin, xmlChar const **attrs)
-{
-	struct color_map_load_state *state = g_new (struct color_map_load_state, 1);
-	state->map = NULL;
-	state->name = NULL;
-	state->lang = NULL;
-	state->langs = g_get_language_names ();
-	state->name_lang_score = G_MAXINT;
-	state->color_stops = NULL;
-	if (!xml)
-		xml = gsf_xml_in_doc_new (color_map_dtd, NULL);
-	for (; attrs && *attrs; attrs +=2)
-		if (!strcmp ((char const *) *attrs, "id")) {
-			state->map = GOG_AXIS_COLOR_MAP (gog_axis_color_map_get_from_id ((char const *) attrs[1]));
-			break;
-		}
-	gsf_xml_in_push_state (xin, xml, state, (GsfXMLInExtDtor) parse_done_cb, attrs);
-}
-
-static void
 color_maps_load_from_dir (char const *path)
 {
 	GDir *dir = g_dir_open (path, 0, NULL);
@@ -824,7 +951,7 @@ gog_axis_color_map_get_from_id (char const *id)
 		if (!strcmp (((GogAxisColorMap *) (ptr->data))->id, id))
 		    return (GogAxisColorMap *) ptr->data;
 	/* create an empty new one */
-	map = g_object_new (GOG_TYPE_AXIS_COLOR_MAP, NULL);
+	map = g_object_new (GOG_TYPE_AXIS_COLOR_MAP, "resource-type", GO_RESOURCE_EXTERNAL, NULL);
 	map->id = g_strdup (id);
 	gog_axis_color_map_registry_add (map);
 	return map;
@@ -836,7 +963,7 @@ _gog_axis_color_maps_init (void)
 	char *path;
 
 	/* Default color map */
-	color_map = g_object_new (GOG_TYPE_AXIS_COLOR_MAP, NULL);
+	color_map = g_object_new (GOG_TYPE_AXIS_COLOR_MAP, "resource-type", GO_RESOURCE_NATIVE, NULL);
 	color_map->id = g_strdup ("Default");
 	color_map->name = g_strdup (N_("Default"));
 	color_map->size = color_map->allocated = 5;
@@ -868,4 +995,5 @@ _gog_axis_color_maps_shutdown (void)
 {
 	g_object_unref (color_map);
 	g_slist_free_full (color_maps, g_object_unref);
+	g_free (xml);
 }
diff --git a/goffice/graph/gog-axis-color-map.h b/goffice/graph/gog-axis-color-map.h
index 5955d7a..fd6c9cb 100644
--- a/goffice/graph/gog-axis-color-map.h
+++ b/goffice/graph/gog-axis-color-map.h
@@ -35,7 +35,10 @@ 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);
+GogAxisColorMap *gog_axis_color_map_from_colors (char const *name, unsigned nb,
+                                                 GOColor const *colors,
+                                                 GoResourceType type);
+GogAxisColorMap *gog_axis_color_map_dup (GogAxisColorMap const *map);
 GdkPixbuf *gog_axis_color_map_get_snapshot (GogAxisColorMap const *map,
                                             gboolean discrete,
                                             gboolean horizontal,
@@ -43,14 +46,13 @@ GdkPixbuf *gog_axis_color_map_get_snapshot (GogAxisColorMap const *map,
                                             unsigned height);
 char const *gog_axis_color_map_get_id (GogAxisColorMap const *map);
 char const *gog_axis_color_map_get_name (GogAxisColorMap const *map);
+GoResourceType gog_axis_color_map_get_resource_type (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_id (char const *id);
-void gog_axis_color_map_write (GogAxisColorMap const *map, GsfXMLOut *output);
-void gog_axis_color_map_sax_push_parser (GsfXMLIn *xin, xmlChar const **attrs);
 
 /* private */
 GogAxisColorMap const *_gog_axis_color_map_get_default (void);
diff --git a/goffice/graph/gog-axis-prefs.ui b/goffice/graph/gog-axis-prefs.ui
index 95513f0..d7e657c 100644
--- a/goffice/graph/gog-axis-prefs.ui
+++ b/goffice/graph/gog-axis-prefs.ui
@@ -750,15 +750,14 @@
           </packing>
         </child>
         <child>
-          <object class="GtkButton" id="edit-btn">
-            <property name="label" translatable="yes">Edit</property>
+          <object class="GtkButton" id="save-btn">
+            <property name="label" translatable="yes">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="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="tooltip_text" translatable="yes">Saves the color map to the local disk.</property>
             <property name="use_action_appearance">False</property>
             <property name="image_position">right</property>
           </object>
diff --git a/goffice/graph/gog-axis.c b/goffice/graph/gog-axis.c
index 8580d11..1159868 100644
--- a/goffice/graph/gog-axis.c
+++ b/goffice/graph/gog-axis.c
@@ -2204,16 +2204,17 @@ static void
 gog_axis_sax_save (GOPersist const *gp, GsfXMLOut *output)
 {
 	GogAxis const *axis;
-	GogGraph *graph;
+	GoResourceType type;
 
 	g_return_if_fail (GOG_IS_AXIS (gp));
 	axis = (GogAxis const*) gp;
 	if (axis->auto_color_map)
 		return;
-	graph = gog_object_get_graph (GOG_OBJECT (gp));
-	if (gog_theme_get_color_map (gog_graph_get_theme (graph), FALSE) == axis->color_map)
+	type = gog_axis_color_map_get_resource_type (axis->color_map);
+	if (type == GO_RESOURCE_CHILD || type == GO_RESOURCE_NATIVE)
 		return;
-	go_doc_save_color_map (gog_graph_get_document (graph), axis->color_map);
+	go_doc_save_resource (gog_graph_get_document (gog_object_get_graph (GOG_OBJECT (gp))),
+	                      GO_PERSIST (axis->color_map));
 }
 
 static void
@@ -2511,6 +2512,7 @@ typedef struct {
 	GtkWidget 	*format_selector;
 	GtkComboBox *color_map_combo;
 	GOCmdContext *cc;
+	GtkBuilder *gui;
 } GogAxisPrefState;
 
 static void
@@ -2727,17 +2729,46 @@ color_map_new_cb (GogAxisPrefState *state)
 }
 
 static void
-color_map_changed_cb (GtkComboBox *combo, GogAxis *axis)
+color_map_dup_cb (GogAxisPrefState *state)
+{
+	GogAxisColorMap *map = gog_axis_color_map_dup (state->axis->color_map);
+	if (gog_axis_color_map_edit (map, state->cc) != NULL) {
+		GtkListStore *model = GTK_LIST_STORE (gtk_combo_box_get_model (state->color_map_combo));
+		GtkTreeIter iter;
+		gtk_list_store_append (model, &iter);
+		gtk_list_store_set (model, &iter,
+		                    0, gog_axis_color_map_get_name (map),
+		                    1, gog_axis_color_map_get_snapshot (map, state->axis->type == GOG_AXIS_PSEUDO_3D,
+		                                                        TRUE, 200, 16),
+		                    2, map,
+		                    -1);
+		gtk_combo_box_set_active_iter (state->color_map_combo, &iter);
+		gog_object_emit_changed (GOG_OBJECT (state->axis), FALSE);
+	} else
+		g_object_unref (map);
+}
+
+static void
+color_map_save_cb (GogAxisPrefState *state)
+{
+	go_persist_sax_save (GO_PERSIST (state->axis->color_map), NULL);
+	gtk_widget_hide (go_gtk_builder_get_widget (state->gui, "save-btn"));
+}
+
+static void
+color_map_changed_cb (GtkComboBox *combo, GogAxisPrefState *state)
 {
 	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)));
+	GogTheme *theme = gog_graph_get_theme (gog_object_get_graph (GOG_OBJECT (state->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);
+	state->axis->color_map = map;
+	state->axis->auto_color_map = map == gog_theme_get_color_map (theme, state->axis->type == GOG_AXIS_PSEUDO_3D);
+	gog_object_emit_changed (GOG_OBJECT (state->axis), FALSE);
+	gtk_widget_set_visible (go_gtk_builder_get_widget (state->gui, "save-btn"),
+	                        gog_axis_color_map_get_resource_type (map) == GO_RESOURCE_EXTERNAL);
 }
 
 struct ColorMapState {
@@ -2784,6 +2815,7 @@ gog_axis_populate_editor (GogObject *gobj,
 	state = g_new0 (GogAxisPrefState, 1);
 	state->axis = axis;
 	state->cc = cc;
+	state->gui = gui;
 	g_object_ref (G_OBJECT (axis));
 
 	/* Bounds Page */
@@ -2929,13 +2961,13 @@ gog_axis_populate_editor (GogObject *gobj,
 		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);
-		g_signal_connect (combo, "changed", G_CALLBACK (color_map_changed_cb), axis);
+		g_signal_connect (combo, "changed", G_CALLBACK (color_map_changed_cb), state);
 		w = go_gtk_builder_get_widget (gui, "new-btn");
 		g_signal_connect_swapped (w, "clicked", G_CALLBACK (color_map_new_cb), state);
 		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);
+		g_signal_connect_swapped (w, "clicked", G_CALLBACK (color_map_dup_cb), state);
+		w = go_gtk_builder_get_widget (gui, "save-btn");
+		g_signal_connect_swapped (w, "clicked", G_CALLBACK (color_map_save_cb), state);
 		go_editor_add_page (editor,
 		                    go_gtk_builder_get_widget (gui, "color-map-grid"),
 		                    _("Colors"));
@@ -2962,9 +2994,10 @@ gog_axis_populate_editor (GogObject *gobj,
 	    }
 	}
 
+	g_signal_connect_swapped (gtk_builder_get_object (gui, "axis-pref-grid"),
+	                          "destroy", G_CALLBACK (g_object_unref), gui);
 	g_object_set_data_full (gtk_builder_get_object (gui, "axis-pref-grid"),
 				"state", state, (GDestroyNotify) gog_axis_pref_state_free);
-	g_object_unref (gui);
 
 	go_editor_set_store_page (editor, &axis_pref_page);
 }
diff --git a/goffice/graph/gog-theme.c b/goffice/graph/gog-theme.c
index e5996c8..72f7f0d 100644
--- a/goffice/graph/gog-theme.c
+++ b/goffice/graph/gog-theme.c
@@ -850,7 +850,7 @@ build_predefined_themes (void)
 	/* builds the default discrete color map */
 	theme->dcm = gog_axis_color_map_from_colors (N_("Theme"),
 	                                             G_N_ELEMENTS (default_palette),
-	                                             default_palette);
+	                                             default_palette, GO_RESOURCE_CHILD);
 
 /* Guppi */
 	theme = gog_theme_new (N_("Guppi"));
@@ -999,7 +999,7 @@ build_predefined_themes (void)
 
 	theme->dcm = gog_axis_color_map_from_colors (N_("Theme"),
 	                                             G_N_ELEMENTS (guppi_palette),
-	                                             guppi_palette);
+	                                             guppi_palette, GO_RESOURCE_CHILD);
 }
 
 struct theme_load_state {
@@ -1135,7 +1135,10 @@ 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", state.theme->palette->len, colors);
+			state.theme->dcm = gog_axis_color_map_from_colors ("Default",
+			                                                   state.theme->palette->len,
+			                                                   colors,
+			                                                   GO_RESOURCE_CHILD);
 			g_free (colors);
 		}
 		state.theme->local_name = state.local_name;
diff --git a/goffice/utils/go-persist.c b/goffice/utils/go-persist.c
index 3235935..8282b81 100644
--- a/goffice/utils/go-persist.c
+++ b/goffice/utils/go-persist.c
@@ -20,7 +20,7 @@
  */
 
 #include <goffice/goffice-config.h>
-#include <goffice/utils/go-persist.h>
+#include <goffice/goffice.h>
 
 
 /**
diff --git a/goffice/utils/goffice-utils.c b/goffice/utils/goffice-utils.c
index 1542141..4ecd594 100644
--- a/goffice/utils/goffice-utils.c
+++ b/goffice/utils/goffice-utils.c
@@ -102,3 +102,31 @@ go_uuid (void)
 
 	return str;
 }
+
+/**
+ * GoResourceType:
+ * @GO_RESOURCE_NATIVE: in goffice code resource.
+ * @GO_RESOURCE_RW: on disk resource which can be overwritten.
+ * @GO_RESOURCE_RO: read only on disk resource.
+ * @GO_RESOURCE_CHILD: child of another resource.
+ * @GO_RESOURCE_EXTERNAL: resource from a data file.
+ * @GO_RESOURCE_INVALID: invalid resource.
+ **/
+GType
+go_resource_type_get_type (void)
+{
+	static GType etype = 0;
+	if (etype == 0) {
+		static GEnumValue const values[] = {
+			{ GO_RESOURCE_NATIVE,	"GO_RESOURCE_NATIVE",	"native" },
+			{ GO_RESOURCE_RW,		"GO_RESOURCE_RW",		"rw" },
+			{ GO_RESOURCE_RO,		"GO_RESOURCE_RO",		"ro" },
+			{ GO_RESOURCE_CHILD,	"GO_RESOURCE_CHILD",	"child" },
+			{ GO_RESOURCE_EXTERNAL,	"GO_RESOURCE_EXTERNAL",	"external" },
+			{ GO_RESOURCE_INVALID,	"GO_RESOURCE_INVALID",	"invalid" },
+			{ 0, NULL, NULL }
+		};
+		etype = g_enum_register_static (g_intern_static_string ("GoResourceType"), values);
+	}
+	return etype;
+}
diff --git a/goffice/utils/goffice-utils.h b/goffice/utils/goffice-utils.h
index 531326f..86daba4 100644
--- a/goffice/utils/goffice-utils.h
+++ b/goffice/utils/goffice-utils.h
@@ -128,6 +128,15 @@ typedef enum {
 	GO_FONT_SCRIPT_SUPER	=  1
 } GOFontScript;
 
+typedef enum {
+	GO_RESOURCE_NATIVE,
+	GO_RESOURCE_RO,
+	GO_RESOURCE_RW,
+	GO_RESOURCE_CHILD,
+	GO_RESOURCE_EXTERNAL,
+	GO_RESOURCE_INVALID
+} GoResourceType;
+
 G_END_DECLS
 
 #include <goffice/goffice.h>
@@ -165,6 +174,7 @@ G_END_DECLS
 #include <goffice/utils/go-units.h>
 #include <goffice/utils/regutf8.h>
 
+GType go_resource_type_get_type (void);
 char *go_uuid (void);
 
 #endif /* GOFFICE_UTILS_H */



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