[goffice] Add initial support for data labels.
- From: Jean BrÃfort <jbrefort src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [goffice] Add initial support for data labels.
- Date: Tue, 2 Aug 2011 17:15:41 +0000 (UTC)
commit 1577667c0c8fbe599f9687b7e303a5aa0da14213
Author: Jean Brefort <jean brefort normalesup org>
Date: Tue Aug 2 19:19:45 2011 +0200
Add initial support for data labels.
ChangeLog | 17 +
goffice/data/go-data-impl.h | 4 +
goffice/data/go-data.c | 107 +++++-
goffice/data/go-data.h | 7 +
goffice/goffice.c | 1 +
goffice/graph/Makefile.am | 7 +-
goffice/graph/goffice-graph.h | 16 +-
goffice/graph/gog-series-impl.h | 3 +
goffice/graph/gog-series-labels-prefs.ui | 227 ++++++++++
goffice/graph/gog-series-labels.c | 720 ++++++++++++++++++++++++++++++
goffice/graph/gog-series-labels.h | 55 +++
goffice/graph/gog-series.c | 26 ++
goffice/graph/gog-theme.c | 20 +
plugins/plot_barcol/gog-1.5d.c | 31 +-
plugins/plot_barcol/gog-barcol.c | 157 ++++++-
plugins/plot_barcol/gog-dropbar.c | 12 +-
plugins/plot_barcol/gog-line.c | 15 +-
plugins/plot_barcol/gog-minmax.c | 12 +-
18 files changed, 1389 insertions(+), 48 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index c5c40d3..fa52a0a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2011-08-02 Jean Brefort <jean brefort normalesup org>
+
+ * goffice/data/go-data-impl.h: add markup support.
+ * goffice/data/go-data.c: ditto.
+ * goffice/data/go-data.h: ditto.
+ * goffice/goffice.c: add initial support for data labels.
+ * goffice/graph/Makefile.am: ditto.
+ * goffice/graph/goffice-graph.h: ditto.
+ * goffice/graph/gog-series-impl.h: ditto.
+ * goffice/graph/gog-series-labels-prefs.ui: ditto.
+ * goffice/graph/gog-series-labels.c: ditto.
+ * goffice/graph/gog-series-labels.h: ditto.
+ * goffice/graph/gog-theme.c: ditto.
+ * plugins/plot_barcol/gog-1.5d.c: add support for d ata labels in bar/col
+ plots.
+ * plugins/plot_barcol/gog-barcol.c: ditto.
+
2011-08-02 Andreas J. Guelzow <aguelzow pyrshep ca>
* goffice/app/go-cmd-context-impl.h (GOCmdContextClass): move
diff --git a/goffice/data/go-data-impl.h b/goffice/data/go-data-impl.h
index c95568c..5ffe415 100644
--- a/goffice/data/go-data-impl.h
+++ b/goffice/data/go-data-impl.h
@@ -57,6 +57,7 @@ typedef struct {
void (*get_bounds) (GOData *data, double *minimum, double *maximum);
double (*get_value) (GOData *data, unsigned int *coordinates);
char * (*get_string) (GOData *data, unsigned int *coordinates);
+ PangoAttrList * (*get_markup) (GOData *data, unsigned int *coordinates);
/* signals */
void (*changed) (GOData *dat);
@@ -72,6 +73,7 @@ typedef struct {
GODataClass base;
double (*get_value) (GODataScalar *scalar);
char const *(*get_str) (GODataScalar *scalar);
+ PangoAttrList *(*get_markup) (GODataScalar *data);
} GODataScalarClass;
#define GO_DATA_VECTOR_LEN_CACHED GO_DATA_SIZE_CACHED
@@ -90,6 +92,7 @@ typedef struct {
void (*load_values) (GODataVector *vec);
double (*get_value) (GODataVector *vec, unsigned i);
char *(*get_str) (GODataVector *vec, unsigned i);
+ PangoAttrList *(*get_markup) (GODataVector *data, unsigned i);
} GODataVectorClass;
#define GO_DATA_MATRIX_SIZE_CACHED GO_DATA_SIZE_CACHED
@@ -109,6 +112,7 @@ typedef struct {
void (*load_values) (GODataMatrix *vec);
double (*get_value) (GODataMatrix *mat, unsigned i, unsigned j);
char *(*get_str) (GODataMatrix *mat, unsigned i, unsigned j);
+ PangoAttrList *(*get_markup) (GODataMatrix *mat, unsigned i, unsigned j);
} GODataMatrixClass;
G_END_DECLS
diff --git a/goffice/data/go-data.c b/goffice/data/go-data.c
index fc56d43..6505e62 100644
--- a/goffice/data/go-data.c
+++ b/goffice/data/go-data.c
@@ -510,6 +510,50 @@ go_data_get_matrix_string (GOData *data, unsigned int row, unsigned int column)
return go_data_get_string (data, 2, coordinates);
}
+static PangoAttrList *
+go_data_get_markup (GOData *data, unsigned int n_coordinates, unsigned int *coordinates)
+{
+ GODataClass const *data_class;
+ unsigned int n_dimensions;
+
+ g_return_val_if_fail (GO_IS_DATA (data), NULL);
+
+ data_class = GO_DATA_GET_CLASS (data);
+
+ n_dimensions = data_class->get_n_dimensions (data);
+ if (n_dimensions != n_coordinates) {
+ g_warning ("[GOData::get_markup] Wrong number of coordinates (given %d - needed %d)",
+ n_coordinates, n_dimensions);
+
+ return NULL;
+ }
+
+ return (data_class->get_markup)? data_class->get_markup (data, coordinates): NULL;
+}
+
+PangoAttrList *
+go_data_get_scalar_markup (GOData *data)
+{
+ return go_data_get_markup (data, 0, NULL);
+}
+
+PangoAttrList *
+go_data_get_vector_markup (GOData *data, unsigned int column)
+{
+ return go_data_get_markup (data, 1, &column);
+}
+
+PangoAttrList *
+go_data_get_matrix_markup (GOData *data, unsigned int row, unsigned int column)
+{
+ unsigned int coordinates[2];
+
+ coordinates[0] = column;
+ coordinates[1] = row;
+
+ return go_data_get_markup (data, 2, coordinates);
+}
+
/*************************************************************************/
#define GO_DATA_SCALAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_TYPE_DATA_SCALAR, GODataScalarClass))
@@ -555,6 +599,12 @@ _data_scalar_get_string (GOData *data, unsigned int *coordinates)
return g_strdup (go_data_scalar_get_str ((GODataScalar *) data));
}
+static PangoAttrList *
+_data_scalar_get_markup (GOData *data, unsigned int *coordinates)
+{
+ return pango_attr_list_copy (go_data_scalar_get_markup ((GODataScalar *) data));
+}
+
static void
go_data_scalar_class_init (GODataClass *data_class)
{
@@ -563,6 +613,7 @@ go_data_scalar_class_init (GODataClass *data_class)
data_class->get_bounds = _data_scalar_get_bounds;
data_class->get_value = _data_scalar_get_value;
data_class->get_string = _data_scalar_get_string;
+ data_class->get_markup = _data_scalar_get_markup;
}
GSF_CLASS_ABSTRACT (GODataScalar, go_data_scalar,
@@ -583,10 +634,18 @@ char const *
go_data_scalar_get_str (GODataScalar *scalar)
{
GODataScalarClass const *klass = GO_DATA_SCALAR_GET_CLASS (scalar);
- g_return_val_if_fail (klass != NULL, NULL);
+ g_return_val_if_fail (klass != NULL, "");
return (*klass->get_str) (scalar);
}
+PangoAttrList *
+go_data_scalar_get_markup (GODataScalar *scalar)
+{
+ GODataScalarClass const *klass = GO_DATA_SCALAR_GET_CLASS (scalar);
+ g_return_val_if_fail (klass != NULL, NULL);
+ return (klass->get_markup)? (*klass->get_markup) (scalar): NULL;
+}
+
/*************************************************************************/
#define GO_DATA_VECTOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_TYPE_DATA_VECTOR, GODataVectorClass))
@@ -637,6 +696,12 @@ _data_vector_get_string (GOData *data, unsigned int *coordinates)
return go_data_vector_get_str ((GODataVector *) data, coordinates[0]);
}
+static PangoAttrList *
+_data_vector_get_markup (GOData *data, unsigned int *coordinates)
+{
+ return go_data_vector_get_markup ((GODataVector *) data, coordinates[0]);
+}
+
static void
go_data_vector_class_init (GODataClass *data_class)
{
@@ -647,6 +712,7 @@ go_data_vector_class_init (GODataClass *data_class)
data_class->get_bounds = _data_vector_get_bounds;
data_class->get_value = _data_vector_get_value;
data_class->get_string = _data_vector_get_string;
+ data_class->get_markup = _data_vector_get_markup;
}
GSF_CLASS_ABSTRACT (GODataVector, go_data_vector,
@@ -719,6 +785,21 @@ go_data_vector_get_str (GODataVector *vec, unsigned i)
return res;
}
+PangoAttrList *
+go_data_vector_get_markup (GODataVector *vec, unsigned i)
+{
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+ if (! (vec->base.flags & GO_DATA_VECTOR_LEN_CACHED)) {
+ (*klass->load_len) (vec);
+ g_return_val_if_fail (vec->base.flags & GO_DATA_VECTOR_LEN_CACHED, NULL);
+ }
+ g_return_val_if_fail ((int)i < vec->len, NULL);
+
+ return (klass->get_markup)? (*klass->get_markup) (vec, i): NULL;
+}
+
void
go_data_vector_get_minmax (GODataVector *vec, double *min, double *max)
{
@@ -816,6 +897,12 @@ _data_matrix_get_string (GOData *data, unsigned int *coordinates)
return go_data_matrix_get_str ((GODataMatrix *) data, coordinates[1], coordinates[0]);
}
+static PangoAttrList *
+_data_matrix_get_markup (GOData *data, unsigned int *coordinates)
+{
+ return go_data_matrix_get_markup ((GODataMatrix *) data, coordinates[1], coordinates[0]);
+}
+
static void
go_data_matrix_class_init (GODataClass *data_class)
{
@@ -826,6 +913,7 @@ go_data_matrix_class_init (GODataClass *data_class)
data_class->get_bounds = _data_matrix_get_bounds;
data_class->get_value = _data_matrix_get_value;
data_class->get_string = _data_matrix_get_string;
+ data_class->get_markup = _data_matrix_get_markup;
}
GSF_CLASS_ABSTRACT (GODataMatrix, go_data_matrix,
@@ -886,7 +974,7 @@ go_data_matrix_get_str (GODataMatrix *mat, unsigned i, unsigned j)
GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
char *res;
- g_return_val_if_fail (klass != NULL, NULL);
+ g_return_val_if_fail (klass != NULL, g_strdup (""));
if (! (mat->base.flags & GO_DATA_MATRIX_SIZE_CACHED)) {
(*klass->load_size) (mat);
g_return_val_if_fail (mat->base.flags & GO_DATA_MATRIX_SIZE_CACHED, g_strdup (""));
@@ -899,6 +987,21 @@ go_data_matrix_get_str (GODataMatrix *mat, unsigned i, unsigned j)
return res;
}
+PangoAttrList *
+go_data_matrix_get_markup (GODataMatrix *mat, unsigned i, unsigned j)
+{
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+ if (! (mat->base.flags & GO_DATA_MATRIX_SIZE_CACHED)) {
+ (*klass->load_size) (mat);
+ g_return_val_if_fail (mat->base.flags & GO_DATA_MATRIX_SIZE_CACHED, NULL);
+ }
+ g_return_val_if_fail (((int)i < mat->size.rows) && ((int)j < mat->size.columns), NULL);
+
+ return (klass->get_markup)? (*klass->get_markup) (mat, i, j): NULL;
+}
+
void
go_data_matrix_get_minmax (GODataMatrix *mat, double *min, double *max)
{
diff --git a/goffice/data/go-data.h b/goffice/data/go-data.h
index 75b0481..ff28ee7 100644
--- a/goffice/data/go-data.h
+++ b/goffice/data/go-data.h
@@ -62,6 +62,10 @@ char * go_data_get_scalar_string (GOData *data);
char * go_data_get_vector_string (GOData *data, unsigned int column);
char * go_data_get_matrix_string (GOData *data, unsigned int row, unsigned int column);
+PangoAttrList * go_data_get_scalar_markup (GOData *data);
+PangoAttrList * go_data_get_vector_markup (GOData *data, unsigned int column);
+PangoAttrList * go_data_get_matrix_markup (GOData *data, unsigned int row, unsigned int column);
+
/*************************************************************************/
#define GO_TYPE_DATA_SCALAR (go_data_scalar_get_type ())
@@ -72,6 +76,7 @@ GType go_data_scalar_get_type (void);
double go_data_scalar_get_value (GODataScalar *val);
char const *go_data_scalar_get_str (GODataScalar *val);
+PangoAttrList *go_data_scalar_get_markup (GODataScalar *val);
/*************************************************************************/
@@ -85,6 +90,7 @@ int go_data_vector_get_len (GODataVector *vec);
double *go_data_vector_get_values (GODataVector *vec);
double go_data_vector_get_value (GODataVector *vec, unsigned i);
char *go_data_vector_get_str (GODataVector *vec, unsigned i);
+PangoAttrList *go_data_vector_get_markup (GODataVector *vec, unsigned i);
void go_data_vector_get_minmax (GODataVector *vec, double *min, double *max);
gboolean go_data_vector_increasing (GODataVector *vec);
gboolean go_data_vector_decreasing (GODataVector *vec);
@@ -102,6 +108,7 @@ GODataMatrixSize go_data_matrix_get_size (GODataMatrix *mat);
double *go_data_matrix_get_values (GODataMatrix *mat);
double go_data_matrix_get_value (GODataMatrix *mat, unsigned i, unsigned j);
char *go_data_matrix_get_str (GODataMatrix *mat, unsigned i, unsigned j);
+PangoAttrList *go_data_matrix_get_markup (GODataMatrix *mat, unsigned i, unsigned j);
void go_data_matrix_get_minmax (GODataMatrix *mat, double *min, double *max);
G_END_DECLS
diff --git a/goffice/goffice.c b/goffice/goffice.c
index 163c446..351ba12 100644
--- a/goffice/goffice.c
+++ b/goffice/goffice.c
@@ -206,6 +206,7 @@ libgoffice_init (void)
#endif
(void) GOG_TYPE_ERROR_BAR;
(void) GOG_TYPE_REG_EQN;
+ (void) GOG_TYPE_SERIES_LABELS;
(void) GOG_TYPE_SERIES_LINES;
(void) GO_TYPE_DATA_SCALAR_VAL;
(void) GO_TYPE_DATA_SCALAR_STR;
diff --git a/goffice/graph/Makefile.am b/goffice/graph/Makefile.am
index c7af691..3efdb3e 100644
--- a/goffice/graph/Makefile.am
+++ b/goffice/graph/Makefile.am
@@ -30,6 +30,7 @@ libgoffice_graph_la_SOURCES = \
gog-trend-line.c \
gog-reg-curve.c \
gog-smoothed-curve.c \
+ gog-series-labels.c \
gog-series-lines.c \
gog-data-set.c \
\
@@ -66,7 +67,8 @@ libgoffice_graph_la_HEADERS = \
gog-error-bar.h \
gog-trend-line.h \
gog-reg-curve.h \
- gog-smoothed-curve.h \
+ gog-smoothed-curve.h \
+ gog-series-labels.h \
gog-series-lines.h \
gog-data-set.h \
gog-renderer.h
@@ -90,8 +92,9 @@ dist_ui_DATA = \
gog-axis-prefs.ui \
gog-error-bar-prefs.ui \
gog-reg-curve-prefs.ui \
- gog-reg-eqn-prefs.ui \
+ gog-reg-eqn-prefs.ui \
gog-series-prefs.ui \
+ gog-series-labels-prefs.ui \
gog-3d-box-prefs.ui
if GOFFICE_WITH_LASEM
diff --git a/goffice/graph/goffice-graph.h b/goffice/graph/goffice-graph.h
index cb04021..2d3a10f 100644
--- a/goffice/graph/goffice-graph.h
+++ b/goffice/graph/goffice-graph.h
@@ -56,7 +56,8 @@ typedef struct _GogEquation GogEquation;
typedef struct _GogRegCurve GogRegCurve;
typedef struct _GogRegEqn GogRegEqn;
typedef struct _GogTrendLineType GogTrendLineType;
-typedef struct _GogSeriesLines GogSeriesLines;
+typedef struct _GogSeriesLines GogSeriesLines;
+typedef struct _GogSeriesLabels GogSeriesLabels;
typedef struct _GogSmoothedCurve GogSmoothedCurve;
typedef struct _Gog3DBox Gog3DBox;
@@ -199,6 +200,18 @@ typedef enum {
GOG_PLOT_RENDERING_BEFORE_GRID
} GogPlotRenderingOrder;
+typedef enum {
+ GOG_SERIES_LABELS_DEFAULT_POS = 0,
+ GOG_SERIES_LABELS_CENTERED = 1,
+ GOG_SERIES_LABELS_TOP = 1 << 1,
+ GOG_SERIES_LABELS_BOTTOM = 1 << 2,
+ GOG_SERIES_LABELS_LEFT = 1 << 3,
+ GOG_SERIES_LABELS_RIGHT = 1 << 4,
+ GOG_SERIES_LABELS_OUTSIDE = 1 << 5,
+ GOG_SERIES_LABELS_INSIDE = 1 << 6,
+ GOG_SERIES_LABELS_NEAR_ORIGIN = 1 << 7,
+} GogSeriesLabelsPos;
+
#define GOG_POSITION_IS_SPECIAL(pos) (((pos) & GOG_POSITION_SPECIAL)&&(!((pos) & GOG_POSITION_MANUAL)))
#define GOG_POSITION_IS_PADDING(pos) (((pos) & GOG_POSITION_PADDING)&&(!((pos) & GOG_POSITION_MANUAL)))
@@ -255,6 +268,7 @@ G_END_DECLS
#include <goffice/graph/gog-reg-curve.h>
#include <goffice/graph/gog-renderer.h>
#include <goffice/graph/gog-series-lines.h>
+#include <goffice/graph/gog-series-labels.h>
#include <goffice/graph/gog-smoothed-curve.h>
#include <goffice/graph/gog-theme.h>
diff --git a/goffice/graph/gog-series-impl.h b/goffice/graph/gog-series-impl.h
index 0b173bb..a7fb959 100644
--- a/goffice/graph/gog-series-impl.h
+++ b/goffice/graph/gog-series-impl.h
@@ -84,6 +84,9 @@ struct _GogSeries {
GOLineInterpolation interpolation;
gboolean interpolation_skip_invalid;
+ /* data related to data labels */
+ GogSeriesLabelsPos default_pos;
+ unsigned allowed_pos; /* if 0, no data labels can be addded */
};
typedef struct {
diff --git a/goffice/graph/gog-series-labels-prefs.ui b/goffice/graph/gog-series-labels-prefs.ui
new file mode 100644
index 0000000..7025550
--- /dev/null
+++ b/goffice/graph/gog-series-labels-prefs.ui
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkListStore" id="available-list">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name dim -->
+ <column type="guint"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="used-list">
+ <columns>
+ <!-- column-name text -->
+ <column type="gchararray"/>
+ <!-- column-name dim -->
+ <column type="guint"/>
+ </columns>
+ </object>
+ <object class="GtkAdjustment" id="offset-adj">
+ <property name="upper">10</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkGrid" id="series-labels-prefs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="row_spacing">12</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="pos-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Position:</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">2</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="offset-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">_Offset:</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">2</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="custom-lbl">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Custom labels:</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">6</property>
+ <property name="width">2</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="offset-btn">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="halign">start</property>
+ <property name="invisible_char">â</property>
+ <property name="invisible_char_set">True</property>
+ <property name="adjustment">offset-adj</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="width">3</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="raise">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-go-up</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="add">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-go-forward</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="remove">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-go-back</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="lower">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-go-down</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTreeView" id="available-tree">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">available-list</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection"/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ <property name="height">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTreeView" id="used-tree">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">used-list</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="width">2</property>
+ <property name="height">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="position-box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="width">3</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/goffice/graph/gog-series-labels.c b/goffice/graph/gog-series-labels.c
new file mode 100644
index 0000000..935d9ec
--- /dev/null
+++ b/goffice/graph/gog-series-labels.c
@@ -0,0 +1,720 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-series-labels.c
+ *
+ * Copyright (C) 2011 Jean Brefort (jean brefort normalesup org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-series-labels.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n-lib.h>
+
+typedef GogOutlinedObjectClass GogSeriesLabelsClass;
+
+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;
+} positions [] =
+{
+ { N_("Centered"), GOG_SERIES_LABELS_CENTERED },
+ { N_("Top"), GOG_SERIES_LABELS_TOP },
+ { N_("Bottom"), GOG_SERIES_LABELS_BOTTOM },
+ { N_("Left"), GOG_SERIES_LABELS_LEFT },
+ { N_("Right"), GOG_SERIES_LABELS_RIGHT },
+ { N_("Outside"), GOG_SERIES_LABELS_OUTSIDE },
+ { N_("Inside"), GOG_SERIES_LABELS_INSIDE },
+ { N_("Near origin"), GOG_SERIES_LABELS_NEAR_ORIGIN }
+};
+
+#ifdef GOFFICE_WITH_GTK
+
+struct SeriesLabelsState {
+ GtkWidget *offset_btn, *offset_lbl;
+ GogSeriesLabels *labels;
+ GtkListStore *avail_list, *used_list;
+ GtkTreeSelection *avail_sel, *used_sel;
+ GtkWidget *raise, *lower, *add, *remove;
+};
+
+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;
+ 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 */
+ first = last = iter;
+ gtk_widget_set_sensitive (state->raise,
+ !gtk_tree_selection_iter_is_selected (state->used_sel, &iter)
+ && gtk_tree_selection_count_selected_rows (state->used_sel)
+ && gtk_tree_model_iter_next (model, &last));
+ /* now find the last iter */
+ do {
+ int dim;
+ char *new_format;
+ last = iter;
+ if (!gtk_tree_selection_iter_is_selected (state->used_sel, &last))
+ count++;
+ 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):
+ g_strdup ("%c");
+ break;
+ case -2:
+ new_format = (state->labels->format)?
+ g_strconcat (state->labels->format, " %l", NULL):
+ g_strdup ("%l");
+ break;
+ default:
+ new_format = (state->labels->format)?
+ g_strdup_printf ("%s %%%d", state->labels->format, dim):
+ g_strdup_printf ("%%%d", dim);
+ }
+ g_free (state->labels->format);
+ state->labels->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);
+ gtk_widget_set_sensitive (state->lower,
+ !gtk_tree_selection_iter_is_selected (state->used_sel, &last)
+ && gtk_tree_selection_count_selected_rows (state->used_sel)
+ && gtk_tree_path_compare (f, l));
+ gtk_tree_path_free (f);
+ gtk_tree_path_free (l);
+ } else {
+ gtk_widget_set_sensitive (state->raise, FALSE);
+ gtk_widget_set_sensitive (state->lower, FALSE);
+ }
+ gtk_widget_set_sensitive (state->remove,
+ count > 0 && gtk_tree_selection_count_selected_rows (state->used_sel));
+}
+
+static void
+add_cb (G_GNUC_UNUSED GtkButton *btn, struct SeriesLabelsState *state)
+{
+ GtkTreeIter iter, add_iter;
+ char *name;
+ int id;
+ gtk_tree_model_get_iter_first (GTK_TREE_MODEL (state->avail_list), &iter);
+ /* we don't need to check if the iter is valid since otherwise,
+ the button would be unsensitive */
+ while (1) {
+ if (gtk_tree_selection_iter_is_selected (state->avail_sel, &iter)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (state->avail_list),
+ &iter, 0, &name, 1, &id, -1);
+ gtk_list_store_append (state->used_list, &add_iter);
+ gtk_list_store_set (state->used_list, &add_iter,
+ 0, name, 1, id, -1);
+ g_free (name);
+ if (!gtk_list_store_remove (state->avail_list, &iter))
+ break;
+ } else if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (state->avail_list), &iter))
+ break;
+ }
+ used_selection_changed_cb (state);
+ gog_object_emit_changed (GOG_OBJECT (state->labels), TRUE);
+}
+
+static void
+remove_cb (G_GNUC_UNUSED GtkButton *btn, struct SeriesLabelsState *state)
+{
+ GtkTreeIter iter, add_iter;
+ char *name;
+ int id;
+ gtk_tree_model_get_iter_first (GTK_TREE_MODEL (state->used_list), &iter);
+ /* we don't need to check if the iter is valid since otherwise,
+ the button would be unsensitive */
+ while (1) {
+ if (gtk_tree_selection_iter_is_selected (state->used_sel, &iter)) {
+ gtk_tree_model_get (GTK_TREE_MODEL (state->used_list),
+ &iter, 0, &name, 1, &id, -1);
+ gtk_list_store_append (state->avail_list, &add_iter);
+ gtk_list_store_set (state->avail_list, &add_iter,
+ 0, name, 1, id, -1);
+ g_free (name);
+ if (!gtk_list_store_remove (state->used_list, &iter))
+ break;
+ } else if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (state->used_list), &iter))
+ break;
+ }
+ used_selection_changed_cb (state);
+ gog_object_emit_changed (GOG_OBJECT (state->labels), TRUE);
+}
+
+static void
+raise_cb (G_GNUC_UNUSED GtkButton *btn, struct SeriesLabelsState *state)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (state->used_list);
+ GtkTreeIter iter, prev;
+ gtk_tree_model_get_iter_first (model, &iter);
+ while (prev = iter, gtk_tree_model_iter_next (model, &iter))
+ if (gtk_tree_selection_iter_is_selected (state->used_sel, &iter))
+ gtk_list_store_move_before (state->used_list, &iter, &prev);
+ gtk_tree_model_get_iter_first (model, &iter);
+ used_selection_changed_cb (state);
+ gog_object_emit_changed (GOG_OBJECT (state->labels), TRUE);
+}
+
+static void
+lower_cb (G_GNUC_UNUSED GtkButton *btn, struct SeriesLabelsState *state)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (state->used_list);
+ GtkTreeIter iter, prev, last;
+ gboolean valid = TRUE;
+ gtk_tree_model_get_iter_first (model, &iter);
+ while (valid) {
+ while (!gtk_tree_selection_iter_is_selected (state->used_sel, &iter)) {
+ if (!gtk_tree_model_iter_next (model, &iter)) {
+ valid = FALSE;
+ break;
+ }
+ }
+ if (!valid)
+ break;
+ prev = last = iter; /* first selected row in the block */
+ while (gtk_tree_selection_iter_is_selected (state->used_sel, &iter)) {
+ if (!gtk_tree_model_iter_next (model, &iter)) {
+ valid = FALSE;
+ break;
+ }
+ last = iter;
+ }
+ if (!valid)
+ break;
+ valid = gtk_tree_model_iter_next (model, &iter);
+ /* at this point,last should be the unselectd row after the selected block */
+ gtk_list_store_move_before (state->used_list, &last, &prev);
+ }
+ used_selection_changed_cb (state);
+ gog_object_emit_changed (GOG_OBJECT (state->labels), TRUE);
+}
+
+static void
+position_changed_cb (GtkComboBox *box, struct SeriesLabelsState *state)
+{
+ GtkTreeModel *model = gtk_combo_box_get_model (box);
+ GtkTreeIter iter;
+ GogSeriesLabelsPos pos;
+
+ 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) {
+ 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);
+ } else {
+ gtk_widget_set_sensitive (state->offset_btn, TRUE);
+ gtk_widget_set_sensitive (state->offset_lbl, TRUE);
+ }
+ }
+}
+
+static void
+offset_changed_cb (GtkSpinButton *btn, GogSeriesLabels *labels)
+{
+ labels->offset = gtk_spin_button_get_value_as_int (btn);
+ gog_object_emit_changed (gog_object_get_parent (GOG_OBJECT (labels)), TRUE);
+}
+
+static void
+avail_selection_changed_cb (struct SeriesLabelsState *state)
+{
+ gtk_widget_set_sensitive (state->add,
+ gtk_tree_selection_count_selected_rows (state->avail_sel));
+}
+
+static void
+gog_series_labels_populate_editor (GogObject *gobj,
+ GOEditor *editor,
+ GogDataAllocator *dalloc,
+ GOCmdContext *cc)
+{
+ GtkBuilder *gui;
+ GtkWidget *labels_prefs, *w;
+ GtkComboBox *box;
+ 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);
+
+ 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;
+
+ box = GTK_COMBO_BOX (go_gtk_builder_get_widget (gui, "position-box"));
+ list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT);
+ gtk_combo_box_set_model (box, GTK_TREE_MODEL (list));
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (box), cell, TRUE);
+ 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) {
+ 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)
+ id = i;
+ if (labels->default_pos == positions[j].pos)
+ def = i;
+ i++;
+ }
+ gtk_combo_box_set_active (box, (id >= 0)? id: def);
+ 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);
+ state->offset_btn = w;
+ state->offset_lbl = go_gtk_builder_get_widget (gui, "offset-label");
+
+ w = go_gtk_builder_get_widget (gui, "available-tree");
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (w), 0, _("Available data"),
+ cell, "text", 0, NULL);
+ state->avail_sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (w));
+ gtk_tree_selection_set_mode (state->avail_sel, GTK_SELECTION_MULTIPLE);
+ g_signal_connect_swapped (G_OBJECT (state->avail_sel), "changed",
+ G_CALLBACK (avail_selection_changed_cb), state);
+ w = go_gtk_builder_get_widget (gui, "used-tree");
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (w), 0, _("Used data"),
+ cell, "text", 0, NULL);
+ state->used_sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (w));
+ gtk_tree_selection_set_mode (state->used_sel, GTK_SELECTION_MULTIPLE);
+ g_signal_connect_swapped (G_OBJECT (state->used_sel), "changed",
+ G_CALLBACK (used_selection_changed_cb), state);
+
+ state->add = go_gtk_builder_get_widget (gui, "add");
+ gtk_widget_set_sensitive (state->add, FALSE);
+ g_signal_connect (G_OBJECT (state->add), "clicked", G_CALLBACK (add_cb), state);
+ state->remove = go_gtk_builder_get_widget (gui, "remove");
+ gtk_widget_set_sensitive (state->remove, FALSE);
+ g_signal_connect (G_OBJECT (state->remove), "clicked", G_CALLBACK (remove_cb), state);
+ state->raise = go_gtk_builder_get_widget (gui, "raise");
+ gtk_widget_set_sensitive (state->raise, FALSE);
+ g_signal_connect (G_OBJECT (state->raise), "clicked", G_CALLBACK (raise_cb), state);
+ state->lower = go_gtk_builder_get_widget (gui, "lower");
+ gtk_widget_set_sensitive (state->lower, FALSE);
+ g_signal_connect (G_OBJECT (state->lower), "clicked", G_CALLBACK (lower_cb), state);
+
+ if (plot != NULL) {
+ /* what should be done if there is no plot, btw is it possible that there is no plot? */
+ GSList *dims = NULL;
+ char *cur;
+ state->avail_list = GTK_LIST_STORE (gtk_builder_get_object (gui, "available-list"));
+ gtk_list_store_clear (state->avail_list); /* GtkBuilder seems to add an empty line */
+ 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;
+ while (*cur) {
+ /* go to next % */
+ while (*cur && *cur != '%')
+ cur = g_utf8_next_char (cur);
+ cur++; /* skip the % */
+ switch (*cur) {
+ case 0:
+ 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);
+ dims = g_slist_prepend (dims, GINT_TO_POINTER (-1));
+ break;
+ case 'l':
+ gtk_list_store_append (state->used_list, &iter);
+ gtk_list_store_set (state->used_list, &iter, 0, _("Legend entry"), 1, -2, -1);
+ dims = g_slist_prepend (dims, GINT_TO_POINTER (-2));
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ unsigned dim = strtoul (cur, &cur, 10);
+ if (dim < plot->desc.series.num_dim)
+ switch (plot->desc.series.dim[j].ms_type) {
+ case GOG_MS_DIM_ERR_plus1:
+ case GOG_MS_DIM_ERR_minus1:
+ case GOG_MS_DIM_ERR_plus2:
+ case GOG_MS_DIM_ERR_minus2:
+ /* we need to eliminate these */
+ break;
+ default:
+ gtk_list_store_append (state->used_list, &iter);
+ gtk_list_store_set (state->used_list, &iter, 0, _(plot->desc.series.dim[dim].name), 1, dim, -1);
+ dims = g_slist_prepend (dims, GINT_TO_POINTER (dim));
+ break;
+ }
+ break;
+ }
+ default:
+ cur = g_utf8_next_char (cur); /* skip unknown character */
+ }
+ }
+ for (j = 0; j < plot->desc.series.num_dim; j++) {
+ switch (plot->desc.series.dim[j].ms_type) {
+ case GOG_MS_DIM_ERR_plus1:
+ case GOG_MS_DIM_ERR_minus1:
+ case GOG_MS_DIM_ERR_plus2:
+ case GOG_MS_DIM_ERR_minus2:
+ /* we need to eliminate these */
+ break;
+ default:
+ if (!g_slist_find (dims, GUINT_TO_POINTER (j))) {
+ gtk_list_store_append (state->avail_list, &iter);
+ gtk_list_store_set (state->avail_list, &iter, 0, _(plot->desc.series.dim[j].name), 1, j, -1);
+ }
+ break;
+ }
+ }
+ 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);
+ }
+ if (!g_slist_find (dims, GINT_TO_POINTER (-2))) {
+ gtk_list_store_append (state->avail_list, &iter);
+ gtk_list_store_set (state->avail_list, &iter, 0, _("Legend entry"), 1, -2, -1);
+ }
+ 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));
+ gtk_widget_show (w);
+ gtk_grid_attach (GTK_GRID (labels_prefs), w, 2, 6, 3, 1);
+
+ g_object_set_data_full (G_OBJECT (labels_prefs), "state", state, g_free);
+
+ 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);
+}
+#endif
+
+static void
+gog_series_labels_init_style (GogStyledObject *gso, GOStyle *style)
+{
+ style->interesting_fields = GO_STYLE_OUTLINE | GO_STYLE_FILL |
+ GO_STYLE_FONT | GO_STYLE_TEXT_LAYOUT;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, GO_STYLE_OUTLINE | GO_STYLE_FILL |
+ GO_STYLE_FONT | GO_STYLE_TEXT_LAYOUT);
+}
+
+static void
+gog_series_labels_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogSeriesLabels *labels = GOG_SERIES_LABELS (obj);
+
+ switch (param_id) {
+ case SERIES_LABELS_PROP_POSITION: {
+ char const *name = g_value_get_string (value);
+ if (!strcmp (name, "centered"))
+ gog_series_labels_set_position (labels, GOG_SERIES_LABELS_CENTERED);
+ else if (!strcmp (name, "top"))
+ gog_series_labels_set_position (labels, GOG_SERIES_LABELS_TOP);
+ else if (!strcmp (name, "bottom"))
+ gog_series_labels_set_position (labels, GOG_SERIES_LABELS_BOTTOM);
+ else if (!strcmp (name, "left"))
+ gog_series_labels_set_position (labels, GOG_SERIES_LABELS_LEFT);
+ else if (!strcmp (name, "right"))
+ gog_series_labels_set_position (labels, GOG_SERIES_LABELS_RIGHT);
+ else if (!strcmp (name, "outside"))
+ gog_series_labels_set_position (labels, GOG_SERIES_LABELS_OUTSIDE);
+ else if (!strcmp (name, "inside"))
+ gog_series_labels_set_position (labels, GOG_SERIES_LABELS_INSIDE);
+ else if (!strcmp (name, "near origin"))
+ gog_series_labels_set_position (labels, GOG_SERIES_LABELS_NEAR_ORIGIN);
+ return;
+ }
+ case SERIES_LABELS_PROP_OFFSET: {
+ unsigned offset = g_value_get_uint (value);
+ if (offset == labels->offset)
+ return;
+ labels->offset = offset;
+ break;
+ case SERIES_LABELS_PROP_FORMAT:
+ g_free (labels->format);
+ labels->format = g_strdup (g_value_get_string (value));
+ 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_series_labels_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogSeriesLabels *labels = GOG_SERIES_LABELS (obj);
+
+ switch (param_id) {
+ case SERIES_LABELS_PROP_POSITION: {
+ char const *posname;
+ switch (labels->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 SERIES_LABELS_PROP_OFFSET:
+ g_value_set_uint (value, labels->offset);
+ break;
+ case SERIES_LABELS_PROP_FORMAT:
+ g_value_set_string (value, labels->format);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_series_labels_parent_changed (GogObject *obj, gboolean was_set)
+{
+ GogSeriesLabels *labels = GOG_SERIES_LABELS (obj);
+ GogPlot *plot;
+ unsigned j;
+
+ if (!was_set)
+ return;
+ plot = (GogPlot *) gog_object_get_parent_typed (obj, GOG_TYPE_PLOT);
+ g_free (labels->format);
+ labels->format = NULL;
+ 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:
+ labels->format = g_strdup_printf ("%%%u", j);
+ j = plot->desc.series.num_dim; /* ensure we exit the loop */
+ break;
+ default:
+ break;
+ }
+ }
+
+}
+
+static void
+gog_series_labels_finalize (GObject *obj)
+{
+ GogSeriesLabels *labels = GOG_SERIES_LABELS (obj);
+ gog_dataset_finalize (GOG_DATASET (obj));
+ g_free (labels->format);
+ series_labels_parent_klass->finalize (obj);
+}
+
+static void
+gog_series_labels_class_init (GObjectClass *obj_klass)
+{
+ GogObjectClass *gog_klass = (GogObjectClass *) obj_klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) obj_klass;
+ series_labels_parent_klass = g_type_class_peek_parent (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,
+ 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, SERIES_LABELS_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, SERIES_LABELS_PROP_FORMAT,
+ g_param_spec_string ("format",
+ _("Format"),
+ _("Label format"),
+ "",
+ GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+
+#ifdef GOFFICE_WITH_GTK
+ gog_klass->populate_editor = gog_series_labels_populate_editor;
+#endif
+ gog_klass->parent_changed = gog_series_labels_parent_changed;
+ style_klass->init_style = gog_series_labels_init_style;
+}
+
+static void
+gog_series_labels_dataset_dims (GogDataset const *set, int *first, int *last)
+{
+ *first = 0;
+ *last = 0;
+}
+
+static GogDatasetElement *
+gog_series_labels_dataset_get_elem (GogDataset const *set, int dim_i)
+{
+ GogSeriesLabels const *sl = GOG_SERIES_LABELS (set);
+ g_return_val_if_fail (0 == dim_i, NULL);
+ return (GogDatasetElement *) &sl->custom_labels;
+}
+
+static void
+gog_series_labels_dataset_dim_changed (GogDataset *set, int dim_i)
+{
+ gog_object_request_update (GOG_OBJECT (set));
+}
+
+static void
+gog_series_labels_dataset_init (GogDatasetClass *iface)
+{
+ iface->get_elem = gog_series_labels_dataset_get_elem;
+ iface->dims = gog_series_labels_dataset_dims;
+ iface->dim_changed = gog_series_labels_dataset_dim_changed;
+}
+
+GSF_CLASS_FULL (GogSeriesLabels, gog_series_labels,
+ NULL, NULL, gog_series_labels_class_init, NULL,
+ NULL, GOG_TYPE_OUTLINED_OBJECT, 0,
+ GSF_INTERFACE (gog_series_labels_dataset_init, GOG_TYPE_DATASET))
+
+void
+gog_series_labels_set_allowed_position (GogSeriesLabels *lbls, unsigned allowed)
+{
+ lbls->allowed_pos = allowed;
+ if ((lbls->position & allowed) == 0 && lbls->position != GOG_SERIES_LABELS_DEFAULT_POS) {
+ lbls->position = GOG_SERIES_LABELS_DEFAULT_POS;
+ gog_object_emit_changed (gog_object_get_parent (GOG_OBJECT (lbls)), TRUE);
+ }
+}
+
+void
+gog_series_labels_set_position (GogSeriesLabels *lbls, GogSeriesLabelsPos pos)
+{
+ 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 ((lbls->allowed_pos & pos) != 0 && lbls->position != pos) {
+ lbls->position = (pos == lbls->default_pos)? GOG_SERIES_LABELS_DEFAULT_POS: pos;
+ if (gog_series_labels_get_position (lbls) == GOG_SERIES_LABELS_CENTERED)
+ lbls->offset = 0;
+ gog_object_emit_changed (gog_object_get_parent (GOG_OBJECT (lbls)), TRUE);
+ }
+}
+
+void
+gog_series_labels_set_default_position (GogSeriesLabels *lbls, GogSeriesLabelsPos pos)
+{
+ 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 (lbls->default_pos != pos) {
+ lbls->default_pos = pos;
+ if ((lbls->allowed_pos & lbls->position) == 0 && lbls->position != GOG_SERIES_LABELS_DEFAULT_POS) {
+ lbls->position = GOG_SERIES_LABELS_DEFAULT_POS;
+ if (pos == GOG_SERIES_LABELS_CENTERED)
+ lbls->offset = 0;
+ }
+ if (lbls->position == GOG_SERIES_LABELS_DEFAULT_POS)
+ gog_object_emit_changed (gog_object_get_parent (GOG_OBJECT (lbls)), TRUE);
+ }
+}
+
+GogSeriesLabelsPos
+gog_series_labels_get_position (GogSeriesLabels const *lbls)
+{
+ 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
new file mode 100644
index 0000000..5fa7585
--- /dev/null
+++ b/goffice/graph/gog-series-labels.h
@@ -0,0 +1,55 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-series-labels.h
+ *
+ * Copyright (C) 2011 Jean Brefort (jean brefort normalesup org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#ifndef GOG_SERIES_LABELS_H
+#define GOG_SERIES_LABELS_H
+
+#include <goffice/goffice.h>
+
+G_BEGIN_DECLS
+
+struct _GogSeriesLabels {
+ GogOutlinedObject base;
+
+ /* private */
+ GogSeriesLabelsPos position;
+ GogSeriesLabelsPos default_pos;
+ unsigned allowed_pos;
+ unsigned offset; /* position offset in pixels */
+ char *format;
+ GogDatasetElement custom_labels;
+};
+
+#define GOG_TYPE_SERIES_LABELS (gog_series_labels_get_type ())
+#define GOG_SERIES_LABELS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_TYPE_SERIES_LABELS, GogSeriesLabels))
+#define GOG_IS_SERIES_LABELS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_TYPE_SERIES_LABELS))
+
+GType gog_series_labels_get_type (void);
+
+void gog_series_labels_set_allowed_position (GogSeriesLabels *lbls, unsigned allowed);
+void gog_series_labels_set_position (GogSeriesLabels *lbls, GogSeriesLabelsPos pos);
+void gog_series_labels_set_default_position (GogSeriesLabels *lbls, GogSeriesLabelsPos pos);
+GogSeriesLabelsPos gog_series_labels_get_position (GogSeriesLabels const *lbls);
+
+G_END_DECLS
+
+#endif /* GOG_SERIES_LABELS_H */
diff --git a/goffice/graph/gog-series.c b/goffice/graph/gog-series.c
index 0b961e4..e954ada 100644
--- a/goffice/graph/gog-series.c
+++ b/goffice/graph/gog-series.c
@@ -300,6 +300,23 @@ role_series_element_pre_remove (GogObject *parent, GogObject *child)
series->overrides = g_list_remove (series->overrides, child);
}
+static gboolean
+role_series_labels_can_add (GogObject const *parent)
+{
+ GogSeries *series = GOG_SERIES (parent);
+
+ return (series->allowed_pos != 0);
+}
+
+static void
+role_series_labels_post_add (GogObject *parent, GogObject *child)
+{
+ GogSeries *series = GOG_SERIES (parent);
+ 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);
+}
+
static void
gog_series_finalize (GObject *obj)
{
@@ -657,6 +674,14 @@ gog_series_class_init (GogSeriesClass *klass)
regression_curve_post_add,
regression_curve_pre_remove,
NULL },
+ { N_("Data labels"), "GogSeriesLabels", 3,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ role_series_labels_can_add,
+ NULL,
+ NULL,
+ role_series_labels_post_add,
+ NULL,
+ NULL }
};
unsigned int i;
@@ -724,6 +749,7 @@ gog_series_init (GogSeries *series)
series->index = -1;
series->acceptable_children = 0;
series->interpolation = GO_LINE_INTERPOLATION_LINEAR;
+ series->default_pos = GOG_SERIES_LABELS_CENTERED;
}
static void
diff --git a/goffice/graph/gog-theme.c b/goffice/graph/gog-theme.c
index 774f001..0df3451 100644
--- a/goffice/graph/gog-theme.c
+++ b/goffice/graph/gog-theme.c
@@ -811,6 +811,16 @@ static void build_predefined_themes (void)
gog_theme_add_element (theme, style,
NULL, "GogRegEqn", NULL);
+ /* series labels */
+ 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, "GogSeriesLabels", NULL);
+
#ifdef GOFFICE_WITH_LASEM
/* Equations */
style = go_style_new ();
@@ -936,6 +946,16 @@ static void build_predefined_themes (void)
gog_theme_add_element (theme, style,
NULL, "GogRegEqn", NULL);
+ /* series labels */
+ 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, "GogSeriesLabels", NULL);
+
#ifdef GOFFICE_WITH_LASEM
/* Equations */
style = go_style_new ();
diff --git a/plugins/plot_barcol/gog-1.5d.c b/plugins/plot_barcol/gog-1.5d.c
index 88d0fab..9284e68 100644
--- a/plugins/plot_barcol/gog-1.5d.c
+++ b/plugins/plot_barcol/gog-1.5d.c
@@ -204,7 +204,7 @@ gog_plot1_5d_update (GogObject *obj)
if (model->fmt == NULL)
model->fmt = go_data_preferred_fmt (series->base.values[1].data);
model->date_conv = go_data_date_conv (series->base.values[1].data);
- index_dim = GOG_SERIES (series)->values[0].data;
+ index_dim = series->base.values[0].data;
}
axis = gog_plot1_5d_get_index_axis (model);
if (model->num_elements != num_elements ||
@@ -391,8 +391,7 @@ series_lines_can_add (GogObject const *parent)
plots and lines with dropbars and high-low lines */
if (GOG_IS_PLOT_BARCOL (plot) && plot->type == GOG_1_5D_NORMAL)
return FALSE;
- return (plot->support_series_lines &&
- !series->has_series_lines);
+ return (plot->support_series_lines && !series->has_series_lines);
}
static void
@@ -483,29 +482,29 @@ gog_series1_5d_update (GogObject *obj)
{
double *vals;
int len = 0;
- GogSeries1_5d *series = GOG_SERIES1_5D (obj);
- unsigned old_num = series->base.num_elements;
+ GogSeries *series = GOG_SERIES (obj);
+ unsigned old_num = series->num_elements;
- if (series->base.values[1].data != NULL) {
- vals = go_data_get_values (series->base.values[1].data);
- len = go_data_get_vector_size (series->base.values[1].data);
+ if (series->values[1].data != NULL) {
+ vals = go_data_get_values (series->values[1].data);
+ len = go_data_get_vector_size (series->values[1].data);
}
- series->base.num_elements = len;
+ series->num_elements = len;
- if (series->base.plot->desc.series.num_dim == 3) {
+ if (series->plot->desc.series.num_dim == 3) {
int tmp = 0;
- if (series->base.values[2].data != NULL) {
- vals = go_data_get_values (series->base.values[2].data);
- tmp = go_data_get_vector_size (series->base.values[2].data);
+ if (series->values[2].data != NULL) {
+ vals = go_data_get_values (series->values[2].data);
+ tmp = go_data_get_vector_size (series->values[2].data);
}
if (tmp < len)
len = tmp;
}
/* queue plot for redraw */
- gog_object_request_update (GOG_OBJECT (series->base.plot));
- if (old_num != series->base.num_elements)
- gog_plot_request_cardinality_update (series->base.plot);
+ gog_object_request_update (GOG_OBJECT (series->plot));
+ if (old_num != series->num_elements)
+ gog_plot_request_cardinality_update (series->plot);
if (gog_series1_5d_parent_klass->update)
gog_series1_5d_parent_klass->update (obj);
diff --git a/plugins/plot_barcol/gog-barcol.c b/plugins/plot_barcol/gog-barcol.c
index a7027b8..a940a01 100644
--- a/plugins/plot_barcol/gog-barcol.c
+++ b/plugins/plot_barcol/gog-barcol.c
@@ -67,8 +67,15 @@ gog_barcol_series_class_init (GogSeriesClass *series_klass)
series_klass->series_element_type = GOG_TYPE_BARCOL_SERIES_ELEMENT;
}
+static void
+gog_barcol_series_init (GogSeries *series)
+{
+ series->allowed_pos = GOG_SERIES_LABELS_CENTERED | GOG_SERIES_LABELS_OUTSIDE
+ | GOG_SERIES_LABELS_INSIDE | GOG_SERIES_LABELS_NEAR_ORIGIN;
+}
+
GSF_DYNAMIC_CLASS (GogBarColSeries, gog_barcol_series,
- gog_barcol_series_class_init, NULL,
+ gog_barcol_series_class_init, gog_barcol_series_init,
GOG_SERIES1_5D_TYPE)
/******************************************************************************/
@@ -368,12 +375,20 @@ typedef struct {
double y;
} ErrorBarData;
+typedef struct {
+ double x;
+ double y;
+ char *str;
+ GOAnchorType anchor;
+} LabelData;
+
static int
gog_barcol_view_get_data_at_point (GogPlotView *view, double x, double y, GogSeries **series)
{
GogBarColPlot const *model = GOG_BARCOL_PLOT (view->base.model);
GogPlot1_5d const *gog_1_5d_model = GOG_PLOT1_5D (model);
GogSeries1_5d const *pseries;
+ GogSeries const *base_series;
GogChart *chart = GOG_CHART (view->base.model->parent);
GogChartMap *chart_map;
GogAxisMap *x_map, *y_map, *map;
@@ -413,10 +428,11 @@ gog_barcol_view_get_data_at_point (GogPlotView *view, double x, double y, GogSer
i = 0;
for (ptr = gog_1_5d_model->base.series ; ptr != NULL ; ptr = ptr->next) {
pseries = ptr->data;
- if (!gog_series_is_valid (GOG_SERIES (pseries)))
+ base_series = GOG_SERIES (pseries);
+ if (!gog_series_is_valid (base_series))
continue;
- vals[i] = go_data_get_values (pseries->base.values[1].data);
- lengths[i] = go_data_get_vector_size (pseries->base.values[1].data);
+ vals[i] = go_data_get_values (base_series->values[1].data);
+ lengths[i] = go_data_get_vector_size (base_series->values[1].data);
i++;
}
@@ -533,13 +549,14 @@ gog_barcol_view_render (GogView *view, GogViewAllocation const *bbox)
GogBarColPlot const *model = GOG_BARCOL_PLOT (view->model);
GogPlot1_5d const *gog_1_5d_model = GOG_PLOT1_5D (view->model);
GogSeries1_5d const *series;
+ GogSeries const *base_series;
GogChart *chart = GOG_CHART (view->model->parent);
GogChartMap *chart_map;
GogViewAllocation work;
GogViewAllocation const *area;
GogRenderer *rend = view->renderer;
GogAxisMap *x_map, *y_map, *map;
- gboolean is_vertical = ! (model->horizontal), valid;
+ gboolean is_vertical = ! (model->horizontal), valid, inverted;
double **vals, sum, neg_base, pos_base, tmp;
double x;
double col_step, group_step, offset, data_scale;
@@ -555,9 +572,11 @@ gog_barcol_view_render (GogView *view, GogViewAllocation const *bbox)
GSList *ptr;
unsigned *lengths;
double plus, minus;
- GogObjectRole const *role = NULL;
+ GogObjectRole const *role = NULL, *lbl_role = NULL;
GogSeriesElement *gse;
GList const **overrides;
+ GogSeriesLabels **labels;
+ LabelData **label_pos;
if (num_elements <= 0 || num_series <= 0)
return;
@@ -576,6 +595,9 @@ gog_barcol_view_render (GogView *view, GogViewAllocation const *bbox)
y_map = gog_chart_map_get_axis_map (chart_map, 1);
map = is_vertical ? y_map : x_map;
+ inverted = gog_axis_is_inverted (is_vertical?
+ GOG_PLOT (model)->axis[GOG_AXIS_Y]:
+ GOG_PLOT (model)->axis[GOG_AXIS_X]);
vals = g_alloca (num_series * sizeof (double *));
lengths = g_alloca (num_series * sizeof (unsigned));
@@ -585,14 +607,19 @@ gog_barcol_view_render (GogView *view, GogViewAllocation const *bbox)
lines = g_alloca (num_series * sizeof (GogSeriesLines *));
paths = g_alloca (num_series * sizeof (GOPath *));
overrides = g_alloca (num_series * sizeof (GSList *));
+ labels = g_alloca (num_series * sizeof (GogSeriesLabels *));
+ label_pos = g_alloca (num_series * sizeof (gpointer));
i = 0;
- for (ptr = gog_1_5d_model->base.series ; ptr != NULL ; ptr = ptr->next) {
+ for (ptr = gog_1_5d_model->base.series ; ptr != NULL ; ptr = ptr->next, i++) {
series = ptr->data;
- if (!gog_series_is_valid (GOG_SERIES (series)))
+ base_series = GOG_SERIES (series);
+ if (!gog_series_is_valid (base_series)) {
+ lengths[i] = 0;
continue;
- vals[i] = go_data_get_values (series->base.values[1].data);
- lengths[i] = go_data_get_vector_size (series->base.values[1].data);
+ }
+ vals[i] = go_data_get_values (base_series->values[1].data);
+ lengths[i] = go_data_get_vector_size (base_series->values[1].data);
styles[i] = GOG_STYLED_OBJECT (series)->style;
errors[i] = series->errors;
overrides[i] = gog_series_get_overrides (GOG_SERIES (series));
@@ -609,7 +636,15 @@ gog_barcol_view_render (GogView *view, GogViewAllocation const *bbox)
paths[i] = go_path_new ();
} else
lines[i] = NULL;
- i++;
+ if (!lbl_role)
+ lbl_role = gog_object_find_role_by_name (GOG_OBJECT (series), "Data labels");
+ labels[i] = (GogSeriesLabels *) gog_object_get_child_by_role (GOG_OBJECT (series), lbl_role);
+ if (labels[i]) {
+ label_pos[i] = g_malloc (sizeof (LabelData) * lengths[i]);
+ for (j = 0; j < lengths[i]; j++)
+ label_pos[i][j].str = go_data_get_vector_string (base_series->values[1].data, j);
+ } else
+ label_pos[i] = NULL;
}
/* work in coordinates drawing bars from the top */
@@ -712,6 +747,92 @@ gog_barcol_view_render (GogView *view, GogViewAllocation const *bbox)
gog_axis_map_to_view (y_map, work.y + work.h));
}
}
+ if (labels[j] != NULL) {
+ unsigned offset;
+ g_object_get (labels[j], "offset", &offset, NULL);
+ switch (gog_series_labels_get_position (labels[j])) {
+ default:
+ case GOG_SERIES_LABELS_CENTERED:
+ label_pos[j][i].anchor = GO_ANCHOR_CENTER;
+ if (is_vertical) {
+ label_pos[j][i].x = gog_axis_map_to_view (x_map, work.y + work.h / 2.);
+ label_pos[j][i].y = gog_axis_map_to_view (y_map, work.x + work.w / 2.);
+ } else {
+ label_pos[j][i].y = gog_axis_map_to_view (y_map, work.y + work.h / 2.);
+ label_pos[j][i].x = gog_axis_map_to_view (x_map, work.x + work.w / 2.);
+ }
+ break;
+ case GOG_SERIES_LABELS_OUTSIDE:
+ if (is_vertical) {
+ label_pos[j][i].x = gog_axis_map_to_view (x_map, work.y + work.h / 2.);
+ label_pos[j][i].y = gog_axis_map_to_view (y_map, work.x + work.w);
+ if (inverted) {
+ label_pos[j][i].anchor = GO_ANCHOR_NORTH;
+ label_pos[j][i].y += offset;
+ } else {
+ label_pos[j][i].anchor = GO_ANCHOR_SOUTH;
+ label_pos[j][i].y -= offset;
+ }
+ } else {
+ label_pos[j][i].y = gog_axis_map_to_view (y_map, work.y + work.h / 2.);
+ label_pos[j][i].x = gog_axis_map_to_view (x_map, work.x + work.w);
+ if (inverted) {
+ label_pos[j][i].anchor = GO_ANCHOR_EAST;
+ label_pos[j][i].x -= offset;
+ } else {
+ label_pos[j][i].anchor = GO_ANCHOR_WEST;
+ label_pos[j][i].x += offset;
+ }
+ }
+ break;
+ case GOG_SERIES_LABELS_INSIDE:
+ if (is_vertical) {
+ label_pos[j][i].x = gog_axis_map_to_view (x_map, work.y + work.h / 2.);
+ label_pos[j][i].y = gog_axis_map_to_view (y_map, work.x + work.w);
+ if (inverted) {
+ label_pos[j][i].anchor = GO_ANCHOR_SOUTH;
+ label_pos[j][i].y -= offset;
+ } else {
+ label_pos[j][i].anchor = GO_ANCHOR_NORTH;
+ label_pos[j][i].y += offset;
+ }
+ } else {
+ label_pos[j][i].y = gog_axis_map_to_view (y_map, work.y + work.h / 2.);
+ label_pos[j][i].x = gog_axis_map_to_view (x_map, work.x + work.w);
+ if (inverted) {
+ label_pos[j][i].anchor = GO_ANCHOR_WEST;
+ label_pos[j][i].x += offset;
+ } else {
+ label_pos[j][i].anchor = GO_ANCHOR_EAST;
+ label_pos[j][i].x -= offset;
+ }
+ }
+ break;
+ case GOG_SERIES_LABELS_NEAR_ORIGIN:
+ if (is_vertical) {
+ label_pos[j][i].x = gog_axis_map_to_view (x_map, work.y + work.h / 2.);
+ label_pos[j][i].y = gog_axis_map_to_view (y_map, work.x);
+ if (inverted) {
+ label_pos[j][i].anchor = GO_ANCHOR_NORTH;
+ label_pos[j][i].y += offset;
+ } else {
+ label_pos[j][i].anchor = GO_ANCHOR_SOUTH;
+ label_pos[j][i].y -= offset;
+ }
+ } else {
+ label_pos[j][i].y = gog_axis_map_to_view (y_map, work.y + work.h / 2.);
+ label_pos[j][i].x = gog_axis_map_to_view (x_map, work.x);
+ if (inverted) {
+ label_pos[j][i].anchor = GO_ANCHOR_EAST;
+ label_pos[j][i].x -= offset;
+ } else {
+ label_pos[j][i].anchor = GO_ANCHOR_WEST;
+ label_pos[j][i].x += offset;
+ }
+ }
+ break;
+ }
+ }
}
}
/*Now draw error bars and clean*/
@@ -733,6 +854,20 @@ gog_barcol_view_render (GogView *view, GogViewAllocation const *bbox)
go_path_free (paths[i]);
}
+ /* Draw data labels if any */
+ for (i = 0; i < num_series; i++)
+ if (labels[i] != NULL) {
+ GogViewAllocation alloc;
+ gog_renderer_push_style (view->renderer, go_styled_object_get_style (GO_STYLED_OBJECT (labels[i])));
+ for (j = 0; j < lengths[i]; j++) {
+ alloc.x = label_pos[i][j].x;
+ alloc.y = label_pos[i][j].y;
+ gog_renderer_draw_text (view->renderer, label_pos[i][j].str, &alloc, label_pos[i][j].anchor, FALSE);
+ g_free (label_pos[i][j].str);
+ }
+ gog_renderer_pop_style (view->renderer);
+ }
+
gog_chart_map_free (chart_map);
}
diff --git a/plugins/plot_barcol/gog-dropbar.c b/plugins/plot_barcol/gog-dropbar.c
index bf4de3c..830b007 100644
--- a/plugins/plot_barcol/gog-dropbar.c
+++ b/plugins/plot_barcol/gog-dropbar.c
@@ -256,6 +256,7 @@ gog_dropbar_view_render (GogView *view, GogViewAllocation const *bbox)
GogBarColPlot const *model = GOG_BARCOL_PLOT (view->model);
GogPlot1_5d const *gog_1_5d_model = GOG_PLOT1_5D (view->model);
GogSeries1_5d const *series;
+ GogSeries const *base_series;
GogAxisMap *x_map, *y_map, *val_map;
GogViewAllocation work;
double *start_vals, *end_vals;
@@ -302,7 +303,8 @@ gog_dropbar_view_render (GogView *view, GogViewAllocation const *bbox)
for (ptr = gog_1_5d_model->base.series ; ptr != NULL ; ptr = ptr->next) {
series = ptr->data;
- if (!gog_series_is_valid (GOG_SERIES (series)))
+ base_series = GOG_SERIES (series);
+ if (!gog_series_is_valid (base_series))
continue;
prec_valid = FALSE;
neg_style = go_style_dup ((GOG_STYLED_OBJECT (series))->style);
@@ -310,10 +312,10 @@ gog_dropbar_view_render (GogView *view, GogViewAllocation const *bbox)
neg_style->fill.pattern.back ^= 0xffffff00;
neg_style->fill.pattern.fore ^= 0xffffff00;
x = offset;
- start_vals = go_data_get_values (series->base.values[1].data);
- n = go_data_get_vector_size (series->base.values[1].data);
- end_vals = go_data_get_values (series->base.values[2].data);
- tmp = go_data_get_vector_size (series->base.values[2].data);
+ start_vals = go_data_get_values (base_series->values[1].data);
+ n = go_data_get_vector_size (base_series->values[1].data);
+ end_vals = go_data_get_values (base_series->values[2].data);
+ tmp = go_data_get_vector_size (base_series->values[2].data);
if (n > tmp)
n = tmp;
diff --git a/plugins/plot_barcol/gog-line.c b/plugins/plot_barcol/gog-line.c
index 06e1595..cd23ded 100644
--- a/plugins/plot_barcol/gog-line.c
+++ b/plugins/plot_barcol/gog-line.c
@@ -164,11 +164,12 @@ static void
gog_line_series_update (GogObject *obj)
{
GogLineSeries *series = GOG_LINE_SERIES (obj);
- unsigned i, nb = series->base.base.num_elements;
+ GogSeries *base_series = GOG_SERIES (obj);
+ unsigned i, nb = base_series->num_elements;
GSList *ptr;
(GOG_OBJECT_CLASS (series_parent_klass))->update (obj);
- if (nb != series->base.base.num_elements) {
- nb = series->base.base.num_elements;
+ if (nb != base_series->num_elements) {
+ nb = base_series->num_elements;
g_free (series->x);
series->x = g_new (double, nb);
for (i = 0; i < nb; i++)
@@ -499,6 +500,7 @@ gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
GogPlot1_5d const *model = GOG_PLOT1_5D (view->model);
GogPlot1_5dType const type = model->type;
GogSeries1_5d const *series;
+ GogSeries const *base_series;
GogChart *chart = GOG_CHART (view->model->parent);
GogChartMap *chart_map;
GogViewAllocation const *area;
@@ -571,15 +573,16 @@ gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
i = 0;
for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
series = ptr->data;
+ base_series = GOG_SERIES (series);
- if (!gog_series_is_valid (GOG_SERIES (series))) {
+ if (!gog_series_is_valid (base_series)) {
vals[i] = NULL;
lengths[i] = 0;
continue;
}
- vals[i] = go_data_get_values (series->base.values[1].data);
- lengths[i] = go_data_get_vector_size (series->base.values[1].data);
+ vals[i] = go_data_get_values (base_series->values[1].data);
+ lengths[i] = go_data_get_vector_size (base_series->values[1].data);
styles[i] = GOG_STYLED_OBJECT (series)->style;
paths[i] = go_path_new ();
diff --git a/plugins/plot_barcol/gog-minmax.c b/plugins/plot_barcol/gog-minmax.c
index a53ff65..868ea6b 100644
--- a/plugins/plot_barcol/gog-minmax.c
+++ b/plugins/plot_barcol/gog-minmax.c
@@ -306,6 +306,7 @@ gog_minmax_view_render (GogView *view, GogViewAllocation const *bbox)
GogMinMaxPlot const *model = GOG_MINMAX_PLOT (view->model);
GogPlot1_5d const *gog_1_5d_model = GOG_PLOT1_5D (view->model);
GogSeries1_5d const *series;
+ GogSeries const *base_series;
GogAxisMap *x_map, *y_map;
gboolean is_vertical = ! (model->horizontal);
double *max_vals, *min_vals;
@@ -345,14 +346,15 @@ gog_minmax_view_render (GogView *view, GogViewAllocation const *bbox)
for (ptr = gog_1_5d_model->base.series ; ptr != NULL ; ptr = ptr->next) {
series = ptr->data;
- if (!gog_series_is_valid (GOG_SERIES (series)))
+ base_series = GOG_SERIES (series);
+ if (!gog_series_is_valid (base_series))
continue;
style = go_styled_object_get_style (GO_STYLED_OBJECT (series));
x = offset;
- min_vals = go_data_get_values (series->base.values[1].data);
- n = go_data_get_vector_size (series->base.values[1].data);
- max_vals = go_data_get_values (series->base.values[2].data);
- tmp = go_data_get_vector_size (series->base.values[2].data);
+ min_vals = go_data_get_values (base_series->values[1].data);
+ n = go_data_get_vector_size (base_series->values[1].data);
+ max_vals = go_data_get_values (base_series->values[2].data);
+ tmp = go_data_get_vector_size (base_series->values[2].data);
if (n > tmp)
n = tmp;
mpath = go_path_new ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]