[goffice] New GogDataLabel object type.



commit 0e28acf5d02c489d98f03bdab2792ce9dcd67d72
Author: Jean Brefort <jean brefort normalesup org>
Date:   Sun Aug 28 19:10:31 2011 +0200

    New GogDataLabel object type.

 ChangeLog                         |   20 +
 goffice/canvas/goc-widget.c       |   10 +-
 goffice/goffice.c                 |    1 +
 goffice/graph/goffice-graph.h     |    1 +
 goffice/graph/gog-renderer.c      |    4 +-
 goffice/graph/gog-series-labels.c |  798 +++++++++++++++++++++++++++++++------
 goffice/graph/gog-series-labels.h |   29 ++
 goffice/graph/gog-series.c        |   40 ++
 goffice/graph/gog-series.h        |    3 +
 goffice/graph/gog-theme.c         |   20 +
 goffice/utils/go-style.c          |   10 +
 goffice/utils/go-style.h          |    1 +
 plugins/plot_barcol/gog-barcol.c  |   12 +-
 plugins/plot_xy/gog-xy.c          |    3 +
 14 files changed, 833 insertions(+), 119 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 0e0d89e..a474b64 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2011-08-28  Jean Brefort  <jean brefort normalesup org>
+
+	* goffice/canvas/goc-widget.c (goc_widget_draw): draw using gtk_widget_draw.
+	* goffice/goffice.c (libgoffice_init): new GogDataLabel type.
+	* goffice/graph/goffice-graph.h: ditto.
+	* goffice/graph/gog-renderer.c (gog_renderer_draw_data_label): use overriden
+	style if given.
+	* goffice/graph/gog-series-labels.c: new GogDataLabel type.
+	* goffice/graph/gog-series-labels.h: ditto.
+	* goffice/graph/gog-series.c (role_series_labels_post_add): set a default
+	format, (gog_series_map_XL_dim), (gog_series_set_XL_dim): new functions.
+	* goffice/graph/gog-series.h: new functions.
+	* goffice/graph/gog-theme.c (build_predefined_themes): new GogDataLabel type
+	* goffice/utils/go-style.c (go_style_is_auto): new function.
+	* goffice/utils/go-style.h: ditto.
+	* plugins/plot_barcol/gog-barcol.c (gog_barcol_view_render): allow for
+	points in data labels.
+	* plugins/plot_xy/gog-xy.c (gog_xy_series_init): set a default label
+	position.
+
 2011-08-27  Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* goffice/utils/go-format.c (go_format_desired_width): check for fontmap
diff --git a/goffice/canvas/goc-widget.c b/goffice/canvas/goc-widget.c
index 688d0f0..c30ad0f 100644
--- a/goffice/canvas/goc-widget.c
+++ b/goffice/canvas/goc-widget.c
@@ -278,9 +278,13 @@ static void
 goc_widget_draw (GocItem const *item, cairo_t *cr)
 {
 	GocWidget *widget = GOC_WIDGET (item);
-	gtk_container_propagate_draw (GTK_CONTAINER (item->canvas),
-				      widget->widget,
-				      cr);
+	int x, y;
+	gtk_container_child_get (GTK_CONTAINER (item->canvas), widget->widget,
+	                         "x", &x, "y", &y, NULL);
+	cairo_save (cr);
+	cairo_translate (cr, x, y);
+	gtk_widget_draw (widget->widget, cr);
+	cairo_restore (cr);
 }
 
 static void
diff --git a/goffice/goffice.c b/goffice/goffice.c
index 351ba12..9163cd1 100644
--- a/goffice/goffice.c
+++ b/goffice/goffice.c
@@ -207,6 +207,7 @@ libgoffice_init (void)
 	(void) GOG_TYPE_ERROR_BAR;
 	(void) GOG_TYPE_REG_EQN;
 	(void) GOG_TYPE_SERIES_LABELS;
+	(void) GOG_TYPE_DATA_LABEL;
 	(void) GOG_TYPE_SERIES_LINES;
 	(void) GO_TYPE_DATA_SCALAR_VAL;
 	(void) GO_TYPE_DATA_SCALAR_STR;
diff --git a/goffice/graph/goffice-graph.h b/goffice/graph/goffice-graph.h
index 3cf1849..f4e6e7a 100644
--- a/goffice/graph/goffice-graph.h
+++ b/goffice/graph/goffice-graph.h
@@ -58,6 +58,7 @@ typedef struct _GogRegEqn	GogRegEqn;
 typedef struct _GogTrendLineType	GogTrendLineType;
 typedef struct _GogSeriesLines  GogSeriesLines;
 typedef struct _GogSeriesLabels GogSeriesLabels;
+typedef struct _GogDataLabel    GogDataLabel;
 typedef struct _GogSmoothedCurve	GogSmoothedCurve;
 typedef struct _Gog3DBox	Gog3DBox;
 
diff --git a/goffice/graph/gog-renderer.c b/goffice/graph/gog-renderer.c
index 90328e6..6ef43ec 100644
--- a/goffice/graph/gog-renderer.c
+++ b/goffice/graph/gog-renderer.c
@@ -1112,7 +1112,9 @@ gog_renderer_draw_data_label (GogRenderer *rend, GogSeriesLabelElt const *elt,
 	g_return_if_fail (rend->cur_style != NULL);
 
 	cairo = rend->cairo;
-	style = rend->cur_style;
+	style = (GO_IS_STYLED_OBJECT (elt->point))?
+		go_styled_object_get_style (GO_STYLED_OBJECT (elt->point)):
+		rend->cur_style;
 
 	/* Note: orig layout may not have been created using cairo! */
 	layout = pango_cairo_create_layout (cairo);
diff --git a/goffice/graph/gog-series-labels.c b/goffice/graph/gog-series-labels.c
index 72ca1ff..35550eb 100644
--- a/goffice/graph/gog-series-labels.c
+++ b/goffice/graph/gog-series-labels.c
@@ -26,17 +26,9 @@
 #include <gsf/gsf-impl-utils.h>
 #include <glib/gi18n-lib.h>
 
-typedef GogOutlinedObjectClass GogSeriesLabelsClass;
-
+static GObjectClass *data_label_parent_klass;
 static GObjectClass *series_labels_parent_klass;
 
-enum {
-	SERIES_LABELS_PROP_0,
-	SERIES_LABELS_PROP_POSITION,
-	SERIES_LABELS_PROP_OFFSET,
-	SERIES_LABELS_PROP_FORMAT
-};
-
 struct {
 	char const *label;
 	GogSeriesLabelsPos pos;
@@ -52,25 +44,84 @@ struct {
 	{ N_("Near origin"), GOG_SERIES_LABELS_NEAR_ORIGIN }
 };
 
+static int
+gog_series_labels_get_valid_element_index (GogSeriesLabels const *lbls, int old_index, int desired_index)
+{
+	int index;
+	GList *ptr;
+	GogSeries *series = GOG_SERIES (gog_object_get_parent (GOG_OBJECT (lbls)));
+
+	g_return_val_if_fail (GOG_IS_SERIES_LABELS (lbls), -1);
+
+	if ((desired_index >= (int) series->num_elements) ||
+	    (desired_index < 0))
+		return old_index;
+
+	if (desired_index > old_index)
+		for (ptr = lbls->overrides; ptr != NULL; ptr = ptr->next) {
+			index = GOG_DATA_LABEL (ptr->data)->index;
+			if (index > desired_index)
+				break;
+			if (index == desired_index)
+				desired_index++;
+		}
+	else
+		for (ptr = g_list_last (series->overrides); ptr != NULL; ptr = ptr->prev) {
+			index = GOG_DATA_LABEL (ptr->data)->index;
+			if (index < desired_index)
+				break;
+			if (index == desired_index)
+				desired_index--;
+		}
+
+	if ((desired_index >= 0) &&
+	    (desired_index < (int) series->num_elements))
+		return desired_index;
+
+	return old_index;
+}
+
 #ifdef GOFFICE_WITH_GTK
 
 struct SeriesLabelsState {
 	GtkWidget *offset_btn, *offset_lbl;
-	GogSeriesLabels *labels;
+	GogObject *labels;
 	GtkListStore *avail_list, *used_list;
 	GtkTreeSelection *avail_sel, *used_sel;
 	GtkWidget *raise, *lower, *add, *remove;
 };
 
 static void
+cb_index_changed (GtkSpinButton *spin_button, GogDataLabel *lbl)
+{
+	int index;
+	int value = gtk_spin_button_get_value (spin_button);
+
+	if ((int) lbl->index == value)
+		return;
+
+	index = gog_series_labels_get_valid_element_index (
+	           GOG_SERIES_LABELS (gog_object_get_parent (GOG_OBJECT (lbl))),
+		   lbl->index, value);
+
+	if (index != value)
+		gtk_spin_button_set_value (spin_button, index);
+
+	g_object_set (lbl, "index", (int) index, NULL);
+}
+
+static void
 used_selection_changed_cb (struct SeriesLabelsState *state)
 {
 	GtkTreeIter iter, last, first;
 	GtkTreePath *f, *l;
 	GtkTreeModel *model = GTK_TREE_MODEL (state->used_list);
 	int count = 0;  /* we count the unselected items to avoid empty labels */
-	g_free (state->labels->format);
-	state->labels->format = NULL;
+	char **format = GOG_IS_DATA_LABEL (state->labels)?
+			&GOG_DATA_LABEL (state->labels)->format:
+			&GOG_SERIES_LABELS (state->labels)->format;
+	g_free (*format);
+	*format = NULL;
 	if (gtk_tree_model_get_iter_first (model, &iter)) {
 		GtkTreeModel *model = GTK_TREE_MODEL (state->used_list);
 		/* if the first row is not selected and a second row exists, set the up button sensitive */
@@ -89,22 +140,22 @@ used_selection_changed_cb (struct SeriesLabelsState *state)
 			gtk_tree_model_get (model, &iter, 1, &dim, -1);
 			switch (dim) {
 			case -1:
-				new_format = (state->labels->format)?
-						g_strconcat (state->labels->format, " %c", NULL):
+				new_format = (*format)?
+						g_strconcat (*format, " %c", NULL):
 						g_strdup ("%c");
 				break;
 			case -2:
-				new_format = (state->labels->format)?
-						g_strconcat (state->labels->format, " %l", NULL):
+				new_format = (*format)?
+						g_strconcat (*format, " %l", NULL):
 						g_strdup ("%l");
 				break;
 			default:
-				new_format = (state->labels->format)?
-						g_strdup_printf ("%s %%%d", state->labels->format, dim):
+				new_format = (*format)?
+						g_strdup_printf ("%s %%%d", *format, dim):
 						g_strdup_printf ("%%%d", dim);
 			}
-			g_free (state->labels->format);
-			state->labels->format = new_format;
+			g_free (*format);
+			*format = new_format;
 		} while (gtk_tree_model_iter_next (model, &iter));
 		f = gtk_tree_model_get_path (model, &first);
 		l = gtk_tree_model_get_path (model, &last);
@@ -120,7 +171,8 @@ used_selection_changed_cb (struct SeriesLabelsState *state)
 	}
 	gtk_widget_set_sensitive (state->remove,
 	                          count > 0 && gtk_tree_selection_count_selected_rows (state->used_sel));
-	gog_object_emit_changed (GOG_OBJECT (state->labels), TRUE);
+	gog_object_request_update (gog_object_get_parent_typed (state->labels, GOG_TYPE_SERIES));
+	gog_object_emit_changed (state->labels, TRUE);
 }
 
 static void
@@ -232,8 +284,16 @@ position_changed_cb (GtkComboBox *box, struct SeriesLabelsState *state)
 
 	if (gtk_combo_box_get_active_iter (box, &iter)) {
 		gtk_tree_model_get (model, &iter, 1, &pos, -1);
-		gog_series_labels_set_position (state->labels, pos);
-		if (gog_series_labels_get_position (state->labels) ==  GOG_SERIES_LABELS_CENTERED) {
+		if (GOG_IS_DATA_LABEL (state->labels)) {
+			GogDataLabel *lbl = GOG_DATA_LABEL (state->labels);
+			gog_data_label_set_position (lbl, pos);
+			pos = gog_data_label_get_position (lbl);
+		} else {
+			GogSeriesLabels *lbls = GOG_SERIES_LABELS (state->labels);
+			gog_series_labels_set_position (lbls, pos);
+			pos = gog_series_labels_get_position (lbls);
+		}
+		if (pos ==  GOG_SERIES_LABELS_CENTERED) {
 			gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->offset_btn), 0);
 			gtk_widget_set_sensitive (state->offset_btn, FALSE);
 			gtk_widget_set_sensitive (state->offset_lbl, FALSE);
@@ -246,10 +306,13 @@ position_changed_cb (GtkComboBox *box, struct SeriesLabelsState *state)
 }
 
 static void
-offset_changed_cb (GtkSpinButton *btn, GogSeriesLabels *labels)
+offset_changed_cb (GtkSpinButton *btn, GogObject *gobj)
 {
-	labels->offset = gtk_spin_button_get_value_as_int (btn);
-	gog_object_emit_changed (gog_object_get_parent (GOG_OBJECT (labels)), TRUE);
+	if (GOG_IS_DATA_LABEL (gobj))
+		GOG_DATA_LABEL (gobj)->offset = gtk_spin_button_get_value_as_int (btn);
+	else
+		GOG_SERIES_LABELS (gobj)->offset = gtk_spin_button_get_value_as_int (btn);
+	gog_object_emit_changed (gobj, TRUE);
 }
 
 static void
@@ -271,15 +334,50 @@ gog_series_labels_populate_editor (GogObject *gobj,
 	GtkListStore *list;
 	GtkCellRenderer *cell;
 	GtkTreeIter iter;
-	GogSeriesLabels *labels = (GogSeriesLabels *) gobj;
 	GogPlot *plot = (GogPlot *) gog_object_get_parent_typed (gobj, GOG_TYPE_PLOT);
 	int i = 0, id = -1, def = 0;
 	unsigned j;
 	struct SeriesLabelsState *state = g_new (struct SeriesLabelsState, 1);
+	GogSeriesLabelsPos position, allowed, default_pos;
+	int offset;
+	char *format;
+	GogObjectClass *parent_class;
+	char const *custom_lbl;
 
 	gui = go_gtk_builder_new ("gog-series-labels-prefs.ui", GETTEXT_PACKAGE, cc);
 	labels_prefs = go_gtk_builder_get_widget (gui, "series-labels-prefs");
-	state->labels = labels;
+	state->labels = gobj;
+
+	if (GOG_IS_DATA_LABEL (gobj)) {
+		GogDataLabel *lbl = GOG_DATA_LABEL (gobj);
+		GtkWidget *w, *grid;
+		position = lbl->position;
+		allowed = lbl->allowed_pos;
+		default_pos = lbl->default_pos;
+		format = lbl->format;
+		offset = lbl->offset;
+		parent_class = GOG_OBJECT_CLASS (data_label_parent_klass);
+		grid = gtk_grid_new ();
+		gtk_grid_set_row_spacing (GTK_GRID (grid), 12);
+		w = gtk_label_new (_("Index:"));
+		gtk_container_add (GTK_CONTAINER (grid), w);
+		w = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), lbl->index);
+		g_signal_connect (G_OBJECT (w), "value_changed",
+		                  G_CALLBACK (cb_index_changed), gobj);
+		gtk_container_add (GTK_CONTAINER (grid), w);
+		gtk_container_add (GTK_CONTAINER (labels_prefs), grid);
+		custom_lbl = _("Custom label");
+	} else {
+		GogSeriesLabels *lbls = GOG_SERIES_LABELS (gobj);
+		position = lbls->position;
+		allowed = lbls->allowed_pos;
+		default_pos = lbls->default_pos;
+		format = lbls->format;
+		offset = lbls->offset;
+		parent_class = GOG_OBJECT_CLASS (series_labels_parent_klass);
+		custom_lbl = _("Custom labels");
+	}
 
 	box = GTK_COMBO_BOX (go_gtk_builder_get_widget (gui, "position-box"));
 	list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT);
@@ -289,12 +387,12 @@ gog_series_labels_populate_editor (GogObject *gobj,
 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (box), cell,
 					"text", 0, NULL);
 	for (j = 0; j < G_N_ELEMENTS (positions); j++)
-		if (labels->allowed_pos & positions[j].pos) {
+		if (allowed & positions[j].pos) {
 			gtk_list_store_append (list, &iter);
 			gtk_list_store_set (list, &iter, 0, _(positions[j].label), 1, positions[j].pos, -1);
-			if (labels->position == positions[j].pos)
+			if (position == positions[j].pos)
 					id = i;
-			if (labels->default_pos == positions[j].pos)
+			if (default_pos == positions[j].pos)
 				def = i;
 			i++;
 		}
@@ -302,8 +400,8 @@ gog_series_labels_populate_editor (GogObject *gobj,
 	g_signal_connect (G_OBJECT (box), "changed", G_CALLBACK (position_changed_cb), state);
 
 	w = go_gtk_builder_get_widget (gui, "offset-btn");
-	gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), labels->offset);
-	g_signal_connect (G_OBJECT (w), "value-changed", G_CALLBACK (offset_changed_cb), labels);
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), offset);
+	g_signal_connect (G_OBJECT (w), "value-changed", G_CALLBACK (offset_changed_cb), gobj);
 	state->offset_btn = w;
 	state->offset_lbl = go_gtk_builder_get_widget (gui, "offset-label");
 
@@ -346,7 +444,7 @@ gog_series_labels_populate_editor (GogObject *gobj,
 		state->used_list = GTK_LIST_STORE (gtk_builder_get_object (gui, "used-list"));
 		gtk_list_store_clear (state->used_list);
 		/* populate used list */
-		cur = labels->format;
+		cur = format;
 		while (*cur) {
 			/* go to next % */
 			while (*cur && *cur != '%')
@@ -357,7 +455,7 @@ gog_series_labels_populate_editor (GogObject *gobj,
 				break; /* protect against a % terminated string */
 			case 'c':
 				gtk_list_store_append (state->used_list, &iter);
-				gtk_list_store_set (state->used_list, &iter, 0, _("Custom labels"), 1, -1, -1);
+				gtk_list_store_set (state->used_list, &iter, 0, custom_lbl, 1, -1, -1);
 				dims = g_slist_prepend (dims, GINT_TO_POINTER (-1));
 				break;
 			case 'l':
@@ -414,7 +512,7 @@ gog_series_labels_populate_editor (GogObject *gobj,
 		}
 		if (!g_slist_find (dims, GINT_TO_POINTER (-1))) {
 			gtk_list_store_append (state->avail_list, &iter);
-			gtk_list_store_set (state->avail_list, &iter, 0, _("Custom labels"), 1, -1, -1);
+			gtk_list_store_set (state->avail_list, &iter, 0, custom_lbl, 1, -1, -1);
 		}
 		if (!g_slist_find (dims, GINT_TO_POINTER (-2))) {
 			gtk_list_store_append (state->avail_list, &iter);
@@ -422,7 +520,8 @@ gog_series_labels_populate_editor (GogObject *gobj,
 		}
 		gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (state->avail_list), 1, GTK_SORT_ASCENDING);
 	}
-	w = GTK_WIDGET (gog_data_allocator_editor (dalloc, GOG_DATASET (gobj), 0, GOG_DATA_VECTOR));
+	w = GTK_WIDGET (gog_data_allocator_editor (dalloc, GOG_DATASET (gobj), 0, 
+	                                           GOG_IS_DATA_LABEL (gobj)? GOG_DATA_SCALAR: GOG_DATA_VECTOR));
 	gtk_widget_show (w);
 	gtk_grid_attach (GTK_GRID (labels_prefs), w, 2, 6, 3, 1);
 
@@ -431,10 +530,20 @@ gog_series_labels_populate_editor (GogObject *gobj,
 	go_editor_add_page (editor,labels_prefs, _("Details"));
 	gtk_widget_show_all (labels_prefs),
 	g_object_unref (gui);
-	(GOG_OBJECT_CLASS(series_labels_parent_klass)->populate_editor) (gobj, editor, dalloc, cc);
+	parent_class->populate_editor (gobj, editor, dalloc, cc);
 }
 #endif
 
+enum {
+	DATA_LABEL_PROP_0,
+	DATA_LABEL_PROP_POSITION,
+	DATA_LABEL_PROP_OFFSET,
+	DATA_LABEL_PROP_FORMAT,
+	DATA_LABEL_PROP_INDEX
+};
+
+typedef GogOutlinedObjectClass GogDataLabelClass;
+
 static void
 gog_series_labels_init_style (GogStyledObject *gso, GOStyle *style)
 {
@@ -445,6 +554,403 @@ gog_series_labels_init_style (GogStyledObject *gso, GOStyle *style)
 	        GO_STYLE_FONT | GO_STYLE_TEXT_LAYOUT);
 }
 
+static gint
+element_compare (GogSeriesElement *gse_a, GogSeriesElement *gse_b)
+{
+	return gse_a->index - gse_b->index;
+}
+
+static void
+gog_data_label_set_property (GObject *obj, guint param_id,
+			 GValue const *value, GParamSpec *pspec)
+{
+	GogDataLabel *label = GOG_DATA_LABEL (obj);
+
+	switch (param_id) {
+	case DATA_LABEL_PROP_POSITION: {
+		char const *name = g_value_get_string (value);
+		if (!strcmp (name, "centered"))
+			gog_data_label_set_position (label, GOG_SERIES_LABELS_CENTERED);
+		else if (!strcmp (name, "top"))
+			gog_data_label_set_position (label, GOG_SERIES_LABELS_TOP);
+		else if (!strcmp (name, "bottom"))
+			gog_data_label_set_position (label, GOG_SERIES_LABELS_BOTTOM);
+		else if (!strcmp (name, "left"))
+			gog_data_label_set_position (label, GOG_SERIES_LABELS_LEFT);
+		else if (!strcmp (name, "right"))
+			gog_data_label_set_position (label, GOG_SERIES_LABELS_RIGHT);
+		else if (!strcmp (name, "outside"))
+			gog_data_label_set_position (label, GOG_SERIES_LABELS_OUTSIDE);
+		else if (!strcmp (name, "inside"))
+			gog_data_label_set_position (label, GOG_SERIES_LABELS_INSIDE);
+		else if (!strcmp (name, "near origin"))
+			gog_data_label_set_position (label, GOG_SERIES_LABELS_NEAR_ORIGIN);
+		return;
+	}
+	case DATA_LABEL_PROP_OFFSET: {
+		unsigned offset = g_value_get_uint (value);
+		if (offset == label->offset)
+			return;
+		label->offset = offset;
+		break;
+	case DATA_LABEL_PROP_FORMAT:
+		g_free (label->format);
+		label->format = g_strdup (g_value_get_string (value));
+		break;
+	}
+	case DATA_LABEL_PROP_INDEX:
+		label->index = g_value_get_int (value);
+		if (GOG_OBJECT (label)->parent != NULL) {
+			GogSeriesLabels *lbls = GOG_SERIES_LABELS (GOG_OBJECT (label)->parent);
+			lbls->overrides = g_list_remove (lbls->overrides, label);
+			lbls->overrides = g_list_insert_sorted (lbls->overrides, label,
+				(GCompareFunc) element_compare);
+		}
+		break;
+
+	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+		 return; /* NOTE : RETURN */
+	}
+
+	gog_object_emit_changed (gog_object_get_parent (GOG_OBJECT (obj)), TRUE);
+}
+
+static void
+gog_data_label_get_property (GObject *obj, guint param_id,
+			 GValue *value, GParamSpec *pspec)
+{
+	GogDataLabel *label = GOG_DATA_LABEL (obj);
+
+	switch (param_id) {
+	case DATA_LABEL_PROP_POSITION: {
+		char const *posname;
+		switch (label->position) {
+		default:
+		case GOG_SERIES_LABELS_DEFAULT_POS:
+			posname = "default";
+			break;
+		case GOG_SERIES_LABELS_CENTERED:
+			posname = "centered";
+			break;
+		case GOG_SERIES_LABELS_TOP:
+			posname = "top";
+			break;
+		case GOG_SERIES_LABELS_BOTTOM:
+			posname = "bottom";
+			break;
+		case GOG_SERIES_LABELS_LEFT:
+			posname = "left";
+			break;
+		case GOG_SERIES_LABELS_RIGHT:
+			posname = "right";
+			break;
+		case GOG_SERIES_LABELS_OUTSIDE:
+			posname = "outside";
+			break;
+		case GOG_SERIES_LABELS_INSIDE:
+			posname = "inside";
+			break;
+		case GOG_SERIES_LABELS_NEAR_ORIGIN:
+			posname = "near origin";
+			break;
+		}
+		g_value_set_string (value, posname);
+		break;
+	}
+	case DATA_LABEL_PROP_OFFSET:
+		g_value_set_uint (value, label->offset);
+		break;
+	case DATA_LABEL_PROP_FORMAT:
+		g_value_set_string (value, label->format);
+		break;
+	case DATA_LABEL_PROP_INDEX:
+		g_value_set_int (value, label->index);
+		break;
+
+	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+		 break;
+	}
+}
+
+static void
+gog_data_label_finalize (GObject *obj)
+{
+	GogDataLabel *label = GOG_DATA_LABEL (obj);
+	gog_dataset_finalize (GOG_DATASET (obj));
+	g_free (label->format);
+	go_string_unref (label->element.str);
+	data_label_parent_klass->finalize (obj);
+}
+
+static void
+gog_data_label_changed (GogObject *obj, gboolean size)
+{
+	gog_object_emit_changed (gog_object_get_parent (obj), size);
+	gog_object_request_update (gog_object_get_parent (obj));
+}
+
+struct attr_closure {
+	PangoAttrList *l;
+	unsigned offset;
+};
+
+static gboolean
+attr_position (PangoAttribute *attr, gpointer data)
+{
+	struct attr_closure *c = (struct attr_closure *) data;
+	PangoAttribute *new_attr = pango_attribute_copy (attr);
+	new_attr->start_index += c->offset;
+	new_attr->end_index += c->offset;
+	pango_attr_list_change (c->l, new_attr);
+	return FALSE;
+}
+
+static void
+gog_data_label_update (GogObject *obj)
+{
+	GogDataLabel *lbl = GOG_DATA_LABEL (obj);
+	GogSeries *series = GOG_SERIES (gog_object_get_parent_typed (obj, GOG_TYPE_SERIES));
+	GString *str = g_string_new ("");
+	unsigned index;
+	PangoAttrList *markup = pango_attr_list_new (), *l;
+	char const *format = lbl->format;
+	char *next;
+	lbl->element.legend_pos = -1;
+	go_string_unref (lbl->element.str);
+	while (*format) {
+		if (*format == '%') {
+			format++;
+			switch (*format) {
+			case 0: /* protect from an unexpected string end */
+				break;
+			case 'c':
+				next = GO_IS_DATA (lbl->custom_label.data)?
+					go_data_get_scalar_string (lbl->custom_label.data):
+					NULL;
+				if (next) {
+					index = str->len;
+					g_string_append (str, next);
+					g_free (next);
+					l = go_data_get_scalar_markup (lbl->custom_label.data);
+					if (l) {
+						struct attr_closure c;
+						c.l = markup;
+						c.offset = index;
+						pango_attr_list_filter (l, attr_position, &c);
+						pango_attr_list_unref (l);
+					}
+				}
+				break;
+			case 'l': {
+				lbl->element.legend_pos = str->len;
+				g_string_append_c (str, ' '); /* this one will be replaced by the legend entry */
+				break;
+			}
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				index = *format - '0';
+				next = go_data_get_vector_string (series->values[index].data, lbl->index);
+				if (next) {
+					g_string_append (str, next);
+					g_free (next);
+				}
+				break;
+			case '%':
+				g_string_append_c (str, '%');
+				break;
+			default:
+				continue;
+			}
+			format++;
+		} else {
+			next = g_utf8_next_char (format);
+			g_string_append_len (str, format, next - format);
+			format = next;
+		}
+	}
+	lbl->element.str = go_string_new_rich (g_string_free (str, FALSE), -1, FALSE, markup, NULL);
+}
+
+static void
+gog_data_label_class_init (GObjectClass *obj_klass)
+{
+	GogObjectClass *gog_klass = (GogObjectClass *) obj_klass;
+	GogStyledObjectClass *style_klass = (GogStyledObjectClass *) obj_klass;
+	data_label_parent_klass = g_type_class_peek_parent (obj_klass);
+
+	obj_klass->set_property	= gog_data_label_set_property;
+	obj_klass->get_property	= gog_data_label_get_property;
+	obj_klass->finalize	= gog_data_label_finalize;
+	/* series labels do not have views, so just forward signals from the plot */
+	gog_klass->use_parent_as_proxy  = TRUE;
+
+	g_object_class_install_property (obj_klass, DATA_LABEL_PROP_POSITION,
+		 g_param_spec_string ("position",
+			_("Position"),
+			_("Position of the label relative to the data graphic element"),
+			"default",
+			GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+        g_object_class_install_property (obj_klass, DATA_LABEL_PROP_OFFSET,
+		 g_param_spec_uint ("offset",
+			_("Offset"),
+			_("Offset to add to the label position"),
+			0, 10, 0,
+			GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+        g_object_class_install_property (obj_klass, DATA_LABEL_PROP_FORMAT,
+		 g_param_spec_string ("format",
+			_("Format"),
+			_("Label format"),
+			"",
+			GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+	g_object_class_install_property (obj_klass, DATA_LABEL_PROP_INDEX,
+		g_param_spec_int ("index",
+			_("Index"),
+			_("Index of the corresponding data element"),
+			0, G_MAXINT, 0,
+			GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT | GOG_PARAM_FORCE_SAVE));
+
+#ifdef GOFFICE_WITH_GTK
+	gog_klass->populate_editor = gog_series_labels_populate_editor;
+#endif
+	gog_klass->changed = gog_data_label_changed;
+	gog_klass->update = gog_data_label_update;
+	style_klass->init_style = gog_series_labels_init_style;
+}
+
+static void
+gog_data_label_dataset_dims (GogDataset const *set, int *first, int *last)
+{
+	*first = 0;
+	*last = 0;
+}
+
+static GogDatasetElement *
+gog_data_label_dataset_get_elem (GogDataset const *set, int dim_i)
+{
+	GogDataLabel const *dl = GOG_DATA_LABEL (set);
+	g_return_val_if_fail (0 == dim_i, NULL);
+	return (GogDatasetElement *) &dl->custom_label;
+}
+
+static void
+gog_data_label_dataset_dim_changed (GogDataset *set, int dim_i)
+{
+	gog_object_request_update (gog_object_get_parent (GOG_OBJECT (set)));
+}
+
+static void
+gog_data_label_dataset_init (GogDatasetClass *iface)
+{
+	iface->get_elem	   = gog_data_label_dataset_get_elem;
+	iface->dims	   = gog_data_label_dataset_dims;
+	iface->dim_changed = gog_data_label_dataset_dim_changed;
+}
+
+GSF_CLASS_FULL (GogDataLabel, gog_data_label,
+		NULL, NULL, gog_data_label_class_init, NULL,
+		NULL, GOG_TYPE_OUTLINED_OBJECT, 0,
+                GSF_INTERFACE (gog_data_label_dataset_init, GOG_TYPE_DATASET))
+
+void gog_data_label_set_allowed_position (GogDataLabel *lbl, unsigned allowed)
+{
+	g_return_if_fail (GOG_IS_DATA_LABEL (lbl));
+	lbl->allowed_pos = allowed;
+	if ((lbl->position & allowed) == 0 && lbl->position != GOG_SERIES_LABELS_DEFAULT_POS) {
+		lbl->position = GOG_SERIES_LABELS_DEFAULT_POS;
+		gog_object_emit_changed (gog_object_get_parent (GOG_OBJECT (lbl)), TRUE);
+	}
+}
+
+void gog_data_label_set_position (GogDataLabel *lbl, GogSeriesLabelsPos pos)
+{
+	g_return_if_fail (GOG_IS_DATA_LABEL (lbl));
+	switch (pos) {
+	case GOG_SERIES_LABELS_DEFAULT_POS:
+	case GOG_SERIES_LABELS_CENTERED:
+	case GOG_SERIES_LABELS_TOP:
+	case GOG_SERIES_LABELS_BOTTOM:
+	case GOG_SERIES_LABELS_LEFT:
+	case GOG_SERIES_LABELS_RIGHT:
+	case GOG_SERIES_LABELS_OUTSIDE:
+	case GOG_SERIES_LABELS_INSIDE:
+	case GOG_SERIES_LABELS_NEAR_ORIGIN:
+		break;
+	default:
+		return;
+	}
+	if ((lbl->allowed_pos & pos) != 0  && lbl->position != pos) {
+		lbl->position = (pos == lbl->default_pos)? GOG_SERIES_LABELS_DEFAULT_POS: pos;
+		if (gog_data_label_get_position (lbl) == GOG_SERIES_LABELS_CENTERED)
+			lbl->offset = 0;
+		gog_object_emit_changed (gog_object_get_parent (GOG_OBJECT (lbl)), TRUE);
+	}
+}
+
+void gog_data_label_set_default_position (GogDataLabel *lbl, GogSeriesLabelsPos pos)
+{
+	g_return_if_fail (GOG_IS_DATA_LABEL (lbl));
+	switch (pos) {
+	case GOG_SERIES_LABELS_CENTERED:
+	case GOG_SERIES_LABELS_TOP:
+	case GOG_SERIES_LABELS_BOTTOM:
+	case GOG_SERIES_LABELS_LEFT:
+	case GOG_SERIES_LABELS_RIGHT:
+	case GOG_SERIES_LABELS_OUTSIDE:
+	case GOG_SERIES_LABELS_INSIDE:
+	case GOG_SERIES_LABELS_NEAR_ORIGIN:
+		break;
+	case GOG_SERIES_LABELS_DEFAULT_POS:
+	default:
+		return;
+	}
+	if (lbl->default_pos != pos) {
+		lbl->default_pos = pos;
+		if ((lbl->allowed_pos & lbl->position) == 0  && lbl->position != GOG_SERIES_LABELS_DEFAULT_POS) {
+			lbl->position = GOG_SERIES_LABELS_DEFAULT_POS;
+			if (pos == GOG_SERIES_LABELS_CENTERED)
+				lbl->offset = 0;
+		}
+		if (lbl->position == GOG_SERIES_LABELS_DEFAULT_POS)
+			gog_object_emit_changed (gog_object_get_parent (GOG_OBJECT (lbl)), TRUE);
+	}
+}
+
+GogSeriesLabelsPos gog_data_label_get_position (GogDataLabel const *lbl)
+{
+	return lbl->position;
+}
+
+GogSeriesLabelElt const *gog_data_label_get_element (GogDataLabel const *lbl)
+{
+	return &lbl->element;
+}
+
+static void
+gog_data_label_set_index (GogDataLabel *lbl, int ind)
+{
+	lbl->index = ind;
+	go_styled_object_apply_theme (GO_STYLED_OBJECT (lbl),
+	                              go_styled_object_get_style (GO_STYLED_OBJECT (lbl)));
+	go_styled_object_style_changed (GO_STYLED_OBJECT (lbl));
+}
+
+typedef GogOutlinedObjectClass GogSeriesLabelsClass;
+
+enum {
+	SERIES_LABELS_PROP_0,
+	SERIES_LABELS_PROP_POSITION,
+	SERIES_LABELS_PROP_OFFSET,
+	SERIES_LABELS_PROP_FORMAT
+};
+
 static void
 gog_series_labels_set_property (GObject *obj, guint param_id,
 			 GValue const *value, GParamSpec *pspec)
@@ -578,28 +1084,13 @@ gog_series_labels_changed (GogObject *obj, gboolean size)
 	gog_object_request_update (gog_object_get_parent (obj));
 }
 
-struct attr_closure {
-	PangoAttrList *l;
-	unsigned offset;
-};
-
-static gboolean
-attr_position (PangoAttribute *attr, gpointer data)
-{
-	struct attr_closure *c = (struct attr_closure *) data;
-	PangoAttribute *new_attr = pango_attribute_copy (attr);
-	new_attr->start_index += c->offset;
-	new_attr->end_index += c->offset;
-	pango_attr_list_change (c->l, new_attr);
-	return FALSE;
-}
-
 static void
 gog_series_labels_update (GogObject *obj)
 {
 	GogSeriesLabels *labels = GOG_SERIES_LABELS (obj);
 	GogObject *parent = gog_object_get_parent (GOG_OBJECT (obj));
 	unsigned i, n;
+	GList *override;
 	if (labels->elements) {
 		n = labels->n_elts;
 		for (i = 0; i < n; i++)
@@ -610,71 +1101,80 @@ gog_series_labels_update (GogObject *obj)
 		GogSeries *series = GOG_SERIES (parent);
 		labels->n_elts = n = gog_series_num_elements (series);
 		labels->elements = g_new0 (GogSeriesLabelElt, n);
+		override = labels->overrides;
 		for (i = 0; i < n; i++) {
-			GString *str = g_string_new ("");
-			unsigned index;
-			PangoAttrList *markup = pango_attr_list_new (), *l;
-			char const *format = labels->format;
-			char *next;
-			labels->elements[i].legend_pos = -1;
-			while (*format) {
-				if (*format == '%') {
-					format++;
-					switch (*format) {
-					case 0: /* protect from an unexpected string end */
-						break;
-					case 'c':
-						next = go_data_get_vector_string (labels->custom_labels.data, i);
-						if (next) {
-							index = str->len;
-							g_string_append (str, next);
-							g_free (next);
-							l = go_data_get_vector_markup (labels->custom_labels.data, i);
-							if (l) {
-								struct attr_closure c;
-								c.l = markup;
-								c.offset = index;
-								pango_attr_list_filter (l, attr_position, &c);
-								pango_attr_list_unref (l);
+			if (override && ((GogDataLabel *) override->data)->index == i) {
+				gog_object_request_update (override->data);
+				gog_object_update (override->data);
+				labels->elements[i].str = go_string_ref (((GogDataLabel *) override->data)->element.str);
+				labels->elements[i].legend_pos = ((GogDataLabel *) override->data)->element.legend_pos;
+				labels->elements[i].point = override->data;
+			} else {
+				GString *str = g_string_new ("");
+				unsigned index;
+				PangoAttrList *markup = pango_attr_list_new (), *l;
+				char const *format = labels->format;
+				char *next;
+				labels->elements[i].legend_pos = -1;
+				while (*format) {
+					if (*format == '%') {
+						format++;
+						switch (*format) {
+						case 0: /* protect from an unexpected string end */
+							break;
+						case 'c':
+							next = go_data_get_vector_string (labels->custom_labels.data, i);
+							if (next) {
+								index = str->len;
+								g_string_append (str, next);
+								g_free (next);
+								l = go_data_get_vector_markup (labels->custom_labels.data, i);
+								if (l) {
+									struct attr_closure c;
+									c.l = markup;
+									c.offset = index;
+									pango_attr_list_filter (l, attr_position, &c);
+									pango_attr_list_unref (l);
+								}
 							}
+							break;
+						case 'l': {
+							labels->elements[i].legend_pos = str->len;
+							g_string_append_c (str, ' '); /* this one will be replaced by the legend entry */
+							break;
 						}
-						break;
-					case 'l': {
-						labels->elements[i].legend_pos = str->len;
-						g_string_append_c (str, ' '); /* this one will be replaced by the legend entry */
-						break;
-					}
-					case '0':
-					case '1':
-					case '2':
-					case '3':
-					case '4':
-					case '5':
-					case '6':
-					case '7':
-					case '8':
-					case '9':
-						index = *format - '0';
-						next = go_data_get_vector_string (series->values[index].data, i);
-						if (next) {
-							g_string_append (str, next);
-							g_free (next);
+						case '0':
+						case '1':
+						case '2':
+						case '3':
+						case '4':
+						case '5':
+						case '6':
+						case '7':
+						case '8':
+						case '9':
+							index = *format - '0';
+							next = go_data_get_vector_string (series->values[index].data, i);
+							if (next) {
+								g_string_append (str, next);
+								g_free (next);
+							}
+							break;
+						case '%':
+							g_string_append_c (str, '%');
+							break;
+						default:
+							continue;
 						}
-						break;
-					case '%':
-						g_string_append_c (str, '%');
-						break;
-					default:
-						continue;
+						format++;
+					} else {
+						next = g_utf8_next_char (format);
+						g_string_append_len (str, format, next - format);
+						format = next;
 					}
-					format++;
-				} else {
-					next = g_utf8_next_char (format);
-					g_string_append_len (str, format, next - format);
-					format = next;
 				}
+				labels->elements[i].str = go_string_new_rich (g_string_free (str, FALSE), -1, FALSE, markup, NULL);
 			}
-			labels->elements[i].str = go_string_new_rich (g_string_free (str, FALSE), -1, FALSE, markup, NULL);
 		}
 	} else
 		labels->elements = NULL; /* FIXME: might be a SeriesElement */
@@ -695,9 +1195,72 @@ gog_series_labels_finalize (GObject *obj)
 	series_labels_parent_klass->finalize (obj);
 }
 
+static gboolean
+role_data_label_can_add (GogObject const *parent)
+{
+	return gog_series_labels_get_valid_element_index(GOG_SERIES_LABELS (parent), -1, 0) >= 0;
+}
+
+static GogObject *
+role_data_label_allocate (GogObject *lbls)
+{
+	GogObject *gse = g_object_new (GOG_TYPE_DATA_LABEL, NULL);
+
+	if (gse != NULL)
+		gog_data_label_set_index (GOG_DATA_LABEL (gse),
+			gog_series_labels_get_valid_element_index (GOG_SERIES_LABELS (lbls), -1, 0));
+	return gse;
+}
+
+static void
+role_data_label_post_add (GogObject *parent, GogObject *child)
+{
+	GogSeriesLabels *lbls = GOG_SERIES_LABELS (parent);
+	GogDataLabel *lbl = GOG_DATA_LABEL (child);
+	GogPlot *plot;
+	unsigned j;
+	go_styled_object_set_style (GO_STYLED_OBJECT (child),
+		go_styled_object_get_style (GO_STYLED_OBJECT (parent)));
+	lbls->overrides = g_list_insert_sorted (lbls->overrides, child,
+		(GCompareFunc) element_compare);
+	plot = (GogPlot *) gog_object_get_parent_typed (child, GOG_TYPE_PLOT);
+	for (j = 0; j < plot->desc.series.num_dim; j++) {
+		/* FIXME, this might depend upon the series type */
+		switch (plot->desc.series.dim[j].ms_type) {
+		case GOG_MS_DIM_VALUES:
+			lbl->format = g_strdup_printf ("%%%u", j);
+			j = plot->desc.series.num_dim; /* ensure we exit the loop */
+			break;
+		default:
+			break;
+		}
+	}
+	lbl->format = g_strdup ("");
+	lbl->default_pos = lbls->default_pos;
+	lbl->allowed_pos = lbls->allowed_pos;
+	lbl->position = lbls->position;
+}
+
+static void
+role_data_label_pre_remove (GogObject *parent, GogObject *child)
+{
+	GogSeriesLabels *lbls = GOG_SERIES_LABELS (parent);
+	lbls->overrides = g_list_remove (lbls->overrides, child);
+}
+
 static void
 gog_series_labels_class_init (GObjectClass *obj_klass)
 {
+	static GogObjectRole const roles[] = {
+		{ N_("Point"), "GogDataLabel",	0,
+		  GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+		  role_data_label_can_add,
+		  NULL,
+		  role_data_label_allocate,
+		  role_data_label_post_add,
+		  role_data_label_pre_remove,
+		  NULL }
+	};
 	GogObjectClass *gog_klass = (GogObjectClass *) obj_klass;
 	GogStyledObjectClass *style_klass = (GogStyledObjectClass *) obj_klass;
 	series_labels_parent_klass = g_type_class_peek_parent (obj_klass);
@@ -705,7 +1268,12 @@ gog_series_labels_class_init (GObjectClass *obj_klass)
 	obj_klass->set_property	= gog_series_labels_set_property;
 	obj_klass->get_property	= gog_series_labels_get_property;
 	obj_klass->finalize	= gog_series_labels_finalize;
-        g_object_class_install_property (obj_klass, SERIES_LABELS_PROP_POSITION,
+	/* series labels do not have views, so just forward signals from the plot */
+	gog_klass->use_parent_as_proxy  = TRUE;
+
+	gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
+
+	g_object_class_install_property (obj_klass, SERIES_LABELS_PROP_POSITION,
 		 g_param_spec_string ("position",
 			_("Position"),
 			_("Position of the label relative to the data graphic element"),
@@ -770,6 +1338,7 @@ GSF_CLASS_FULL (GogSeriesLabels, gog_series_labels,
 void
 gog_series_labels_set_allowed_position (GogSeriesLabels *lbls, unsigned allowed)
 {
+	g_return_if_fail (GOG_IS_SERIES_LABELS (lbls));
 	lbls->allowed_pos = allowed;
 	if ((lbls->position & allowed) == 0 && lbls->position != GOG_SERIES_LABELS_DEFAULT_POS) {
 		lbls->position = GOG_SERIES_LABELS_DEFAULT_POS;
@@ -780,6 +1349,7 @@ gog_series_labels_set_allowed_position (GogSeriesLabels *lbls, unsigned allowed)
 void
 gog_series_labels_set_position (GogSeriesLabels *lbls, GogSeriesLabelsPos pos)
 {
+	g_return_if_fail (GOG_IS_SERIES_LABELS (lbls));
 	switch (pos) {
 	case GOG_SERIES_LABELS_DEFAULT_POS:
 	case GOG_SERIES_LABELS_CENTERED:
@@ -805,6 +1375,7 @@ gog_series_labels_set_position (GogSeriesLabels *lbls, GogSeriesLabelsPos pos)
 void
 gog_series_labels_set_default_position (GogSeriesLabels *lbls, GogSeriesLabelsPos pos)
 {
+	g_return_if_fail (GOG_IS_SERIES_LABELS (lbls));
 	switch (pos) {
 	case GOG_SERIES_LABELS_CENTERED:
 	case GOG_SERIES_LABELS_TOP:
@@ -834,6 +1405,7 @@ gog_series_labels_set_default_position (GogSeriesLabels *lbls, GogSeriesLabelsPo
 GogSeriesLabelsPos
 gog_series_labels_get_position (GogSeriesLabels const *lbls)
 {
+	g_return_val_if_fail (GOG_IS_SERIES_LABELS (lbls), GOG_SERIES_LABELS_DEFAULT_POS);
 	return (lbls->position == GOG_SERIES_LABELS_DEFAULT_POS)?
 		lbls->default_pos: lbls->position;
 }
diff --git a/goffice/graph/gog-series-labels.h b/goffice/graph/gog-series-labels.h
index deed348..cd4216a 100644
--- a/goffice/graph/gog-series-labels.h
+++ b/goffice/graph/gog-series-labels.h
@@ -30,8 +30,36 @@ G_BEGIN_DECLS
 typedef struct  {
 	GOString *str;
 	int legend_pos;
+	GogObject *point;
 } GogSeriesLabelElt;
 
+
+struct _GogDataLabel {
+	GogOutlinedObject base;
+
+	/* private */
+	unsigned index;
+	GogSeriesLabelsPos position;
+	GogSeriesLabelsPos default_pos;
+	unsigned allowed_pos;
+	unsigned offset; /* position offset in pixels */
+	char *format;
+	GogDatasetElement custom_label;
+	GogSeriesLabelElt element;
+};
+
+#define GOG_TYPE_DATA_LABEL		(gog_data_label_get_type ())
+#define GOG_DATA_LABEL(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_TYPE_DATA_LABEL, GogDataLabel))
+#define GOG_IS_DATA_LABEL(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_TYPE_DATA_LABEL))
+
+GType gog_data_label_get_type (void);
+
+void gog_data_label_set_allowed_position (GogDataLabel *lbl, unsigned allowed);
+void gog_data_label_set_position (GogDataLabel *lbl, GogSeriesLabelsPos pos);
+void gog_data_label_set_default_position (GogDataLabel *lbl, GogSeriesLabelsPos pos);
+GogSeriesLabelsPos gog_data_label_get_position (GogDataLabel const *lbl);
+GogSeriesLabelElt const *gog_data_label_get_element (GogDataLabel const *lbl);
+
 struct _GogSeriesLabels {
 	GogOutlinedObject base;
 
@@ -44,6 +72,7 @@ struct _GogSeriesLabels {
 	GogDatasetElement custom_labels;
 	unsigned n_elts;
 	GogSeriesLabelElt *elements;
+	GList *overrides;
 };
 
 #define GOG_TYPE_SERIES_LABELS		(gog_series_labels_get_type ())
diff --git a/goffice/graph/gog-series.c b/goffice/graph/gog-series.c
index f770478..5fe5371 100644
--- a/goffice/graph/gog-series.c
+++ b/goffice/graph/gog-series.c
@@ -312,9 +312,22 @@ static void
 role_series_labels_post_add (GogObject *parent, GogObject *child)
 {
 	GogSeries *series = GOG_SERIES (parent);
+	GogSeriesDesc const *desc;
+	unsigned i;
 	GogSeriesLabels *labels = GOG_SERIES_LABELS (child);
 	gog_series_labels_set_allowed_position (labels, series->allowed_pos);
 	gog_series_labels_set_default_position (labels, series->default_pos);
+	/* default is to show values */
+
+	/* Are there any shared dimensions */
+	desc = &series->plot->desc.series;
+	for (i = 0; i < desc->num_dim; i++)
+		if (desc->dim[i].ms_type == GOG_MS_DIM_VALUES)
+			break;
+	labels->format = (i != desc->num_dim)?
+		                            g_strdup_printf ("%%%u", i):
+		                            g_strdup ("");
+	
 }
 
 static void
@@ -1012,6 +1025,33 @@ gog_series_set_dim (GogSeries *series, int dim_i, GOData *val, GError **err)
 	gog_dataset_set_dim (GOG_DATASET (series), dim_i, val, err);
 }
 
+int
+gog_series_map_XL_dim (GogSeries const *series, GogMSDimType ms_type)
+{
+	GogSeriesDesc const *desc = &series->plot->desc.series;
+	unsigned i = desc->num_dim;
+
+	if (ms_type == GOG_MS_DIM_LABELS)
+		return -1;
+	while (i-- > 0)
+		if (desc->dim[i].ms_type == ms_type)
+			return i;
+	return -2;
+}
+
+void
+gog_series_set_XL_dim (GogSeries *series, GogMSDimType ms_type, GOData *val, GError **err)
+{
+	int dim;
+	g_return_if_fail (series !=NULL);
+	dim = gog_series_map_XL_dim (series, ms_type);
+	if (dim >= -1) {
+		gog_series_set_dim (series, dim, val, err);
+		return;
+	}
+	g_object_unref (val);
+}
+
 /**
  * gog_series_num_elements :
  * @series : #GogSeries
diff --git a/goffice/graph/gog-series.h b/goffice/graph/gog-series.h
index dde71f3..51dceca 100644
--- a/goffice/graph/gog-series.h
+++ b/goffice/graph/gog-series.h
@@ -58,6 +58,9 @@ void	      gog_series_set_name   (GogSeries *series,
 				     GODataScalar *name_src, GError **err);
 void	      gog_series_set_dim    (GogSeries *series, int dim_i,
 				     GOData *val, GError **err);
+void          gog_series_set_XL_dim (GogSeries *series, GogMSDimType ms_type,
+                                     GOData *val, GError **err);
+int           gog_series_map_XL_dim (GogSeries const *series, GogMSDimType ms_type);
 void	      gog_series_set_index  (GogSeries *series,
 				     int ind, gboolean is_manual);
 
diff --git a/goffice/graph/gog-theme.c b/goffice/graph/gog-theme.c
index 0df3451..9799efc 100644
--- a/goffice/graph/gog-theme.c
+++ b/goffice/graph/gog-theme.c
@@ -821,6 +821,16 @@ static void build_predefined_themes (void)
 	go_style_set_font_desc (style, pango_font_description_from_string ("Sans 6"));
 	gog_theme_add_element (theme, style, NULL, "GogSeriesLabels", NULL);
 
+	/* data label */
+	style = go_style_new ();
+	style->line.dash_type = GO_LINE_NONE;
+	style->line.width = 0; /* none */
+	style->line.color = GO_COLOR_BLACK;
+	style->fill.type = GO_STYLE_FILL_NONE;
+	go_pattern_set_solid (&style->fill.pattern, GO_COLOR_WHITE);
+	go_style_set_font_desc (style, pango_font_description_from_string ("Sans 6"));
+	gog_theme_add_element (theme, style, NULL, "GogDataLabel", NULL);
+
 #ifdef GOFFICE_WITH_LASEM
 	/* Equations */
 	style = go_style_new ();
@@ -956,6 +966,16 @@ static void build_predefined_themes (void)
 	go_style_set_font_desc (style, pango_font_description_from_string ("Sans 6"));
 	gog_theme_add_element (theme, style, NULL, "GogSeriesLabels", NULL);
 
+	/* data label */
+	style = go_style_new ();
+	style->line.dash_type = GO_LINE_NONE;
+	style->line.width = 0; /* none */
+	style->line.color = GO_COLOR_BLACK;
+	style->fill.type = GO_STYLE_FILL_NONE;
+	go_pattern_set_solid (&style->fill.pattern, GO_COLOR_WHITE);
+	go_style_set_font_desc (style, pango_font_description_from_string ("Sans 6"));
+	gog_theme_add_element (theme, style, NULL, "GogDataLabel", NULL);
+
 #ifdef GOFFICE_WITH_LASEM
 	/* Equations */
 	style = go_style_new ();
diff --git a/goffice/utils/go-style.c b/goffice/utils/go-style.c
index bd102bd..c00cd54 100644
--- a/goffice/utils/go-style.c
+++ b/goffice/utils/go-style.c
@@ -2005,6 +2005,16 @@ go_style_force_auto (GOStyle *style)
 	style->text_layout.auto_angle = TRUE;
 }
 
+gboolean
+go_style_is_auto (GOStyle *style)
+{
+	return style->marker.auto_shape && style->marker.auto_outline_color &&
+	       style->marker.auto_fill_color && style->line.auto_dash &&
+	       style->line.auto_color && style->fill.auto_type &&
+	       style->fill.auto_fore && style->fill.auto_back &&
+	       style->font.auto_scale && style->text_layout.auto_angle;
+}
+
 /**
  * go_style_set_marker :
  * @style : #GOStyle
diff --git a/goffice/utils/go-style.h b/goffice/utils/go-style.h
index b9b415c..419509d 100644
--- a/goffice/utils/go-style.h
+++ b/goffice/utils/go-style.h
@@ -139,6 +139,7 @@ gboolean   go_style_is_line_visible	(GOStyle const *style);
 gboolean   go_style_is_outline_visible	(GOStyle const *style);
 gboolean   go_style_is_fill_visible	(GOStyle const *style);
 void	   go_style_force_auto		(GOStyle *style);
+gboolean   go_style_is_auto		(GOStyle *style);
 
 #ifdef GOFFICE_WITH_GTK
 void 	   go_style_populate_editor 	(GOStyle *style,
diff --git a/plugins/plot_barcol/gog-barcol.c b/plugins/plot_barcol/gog-barcol.c
index a07b055..8dbd0e0 100644
--- a/plugins/plot_barcol/gog-barcol.c
+++ b/plugins/plot_barcol/gog-barcol.c
@@ -749,8 +749,16 @@ gog_barcol_view_render (GogView *view, GogViewAllocation const *bbox)
 			}
 			if (labels[j] != NULL) {
 				unsigned offset;
-				g_object_get (labels[j], "offset", &offset, NULL);
-				switch (gog_series_labels_get_position (labels[j])) {
+				GogSeriesLabelsPos position;
+				GogObject *point = label_pos[j][i].elt->point;
+				if (point) {
+					g_object_get (point, "offset", &offset, NULL);
+					position = gog_data_label_get_position (GOG_DATA_LABEL (point));
+				} else {
+					g_object_get (labels[j], "offset", &offset, NULL);
+					position = gog_series_labels_get_position (labels[j]);
+				}
+				switch (position) {
 				default:
 				case GOG_SERIES_LABELS_CENTERED:
 					label_pos[j][i].anchor = GO_ANCHOR_CENTER;
diff --git a/plugins/plot_xy/gog-xy.c b/plugins/plot_xy/gog-xy.c
index d9d442a..0f08a08 100644
--- a/plugins/plot_xy/gog-xy.c
+++ b/plugins/plot_xy/gog-xy.c
@@ -1800,6 +1800,9 @@ gog_xy_series_init (GObject *obj)
 	GOG_XY_INTERPOLATION_CLAMPS (series->interpolation_props)->series = series;
 	gog_dataset_set_dim (series->interpolation_props, 0, go_data_scalar_val_new (0.), NULL);
 	gog_dataset_set_dim (series->interpolation_props, 1, go_data_scalar_val_new (0.), NULL);
+	GOG_SERIES (series)->allowed_pos = GOG_SERIES_LABELS_CENTERED | GOG_SERIES_LABELS_LEFT
+		| GOG_SERIES_LABELS_RIGHT | GOG_SERIES_LABELS_TOP | GOG_SERIES_LABELS_BOTTOM;
+	GOG_SERIES (series)->default_pos = GOG_SERIES_LABELS_TOP;
 }
 
 static void



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