[goffice] Make chart position persistent in auto mode. [#152674]



commit b2edcabf1807ced28bb12d8f2619c1ba8cb8c477
Author: Jean Brefort <jean brefort normalesup org>
Date:   Thu Oct 29 17:38:22 2009 +0100

    Make chart position persistent in auto mode. [#152674]

 ChangeLog                         |   15 ++++
 NEWS                              |    1 +
 goffice/graph/gog-chart-impl.h    |    5 +-
 goffice/graph/gog-chart.c         |   75 +++++++++++++++--
 goffice/graph/gog-graph.c         |   57 ++++++++-----
 goffice/graph/gog-object-prefs.ui |  162 +++++++++++++++++++++++++++++++++++++
 goffice/graph/gog-object.c        |   35 ++++++++-
 7 files changed, 317 insertions(+), 33 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 16e211c..7673940 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
 2009-10-29  Jean Brefort  <jean brefort normalesup org>
 
+	* goffice/graph/gog-chart-impl.h: add persistent position for auto mode.
+	Fixes #152674.
+	* goffice/graph/gog-chart.c (gog_chart_set_property),
+	(gog_chart_get_property), (gog_chart_class_init), (gog_chart_init),
+	(gog_chart_get_position), (gog_chart_set_position): ditto.
+	* goffice/graph/gog-graph.c (role_chart_post_add),
+	(gog_graph_validate_chart_layout), (gog_graph_view_size_allocate):
+	* goffice/graph/gog-object-prefs.ui: add a user interface to set column
+	and row position of a chart in the graph.
+	* goffice/graph/gog-object.c (update_select_state),
+	(cb_manual_position_changed), (cb_chart_position_changed),
+	(gog_object_populate_editor): ditto.
+
+2009-10-29  Jean Brefort  <jean brefort normalesup org>
+
 	* goffice/canvas/goc-graph.c (goc_graph_update_bounds): set the right size.
 	* goffice/graph/gog-renderer.c (gog_renderer_update): fixed resizing a
 	graph. [#599887]
diff --git a/NEWS b/NEWS
index 36b33d2..861af79 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ Jean:
 	* Make titles the first logical child and ensure they are rendered last.
 	Really fixes #152675.
 	* Fix graph scale after a size change. [#599887]
+	* Make chart position persistent in auto mode. [#152674]
 
 Morten:
 	* Canvas improvements for arrows.
diff --git a/goffice/graph/gog-chart-impl.h b/goffice/graph/gog-chart-impl.h
index 9c8bd80..46b3fb1 100644
--- a/goffice/graph/gog-chart-impl.h
+++ b/goffice/graph/gog-chart-impl.h
@@ -34,7 +34,10 @@ struct _GogChart {
 	gboolean cardinality_valid;
 
 	/* use a simple grid layout to position charts within graph */
-	unsigned x, y, cols, rows;
+	unsigned x_pos, y_pos;
+	unsigned cols, rows; 		/* if == 0, chart is not positionned */
+	/* actual chart position in graph grid after layout validation */
+	unsigned x_pos_actual, y_pos_actual;
 
 	GogObject *grid;
 	GSList  *axes;
diff --git a/goffice/graph/gog-chart.c b/goffice/graph/gog-chart.c
index 7e264ac..38c1352 100644
--- a/goffice/graph/gog-chart.c
+++ b/goffice/graph/gog-chart.c
@@ -90,6 +90,10 @@ enum {
 	CHART_PROP_CARDINALITY_VALID,
 	CHART_PROP_PLOT_AREA,
 	CHART_PROP_PLOT_AREA_IS_MANUAL,
+	CHART_PROP_X_POS,
+	CHART_PROP_Y_POS,
+	CHART_PROP_ROWS,
+	CHART_PROP_COLUMNS
 };
 
 static GType gog_chart_view_get_type (void);
@@ -128,6 +132,7 @@ gog_chart_set_property (GObject *obj, guint param_id,
 	GogChart *chart = GOG_CHART (obj);
 	char **str_doubles;
 	char const *str;
+	gboolean changed = FALSE;
 
 	switch (param_id) {
 	case CHART_PROP_PLOT_AREA:
@@ -146,9 +151,30 @@ gog_chart_set_property (GObject *obj, guint param_id,
 	case CHART_PROP_PLOT_AREA_IS_MANUAL:
 		chart->is_plot_area_manual = g_value_get_boolean (value);
 		break;
+	case CHART_PROP_X_POS:
+		chart->x_pos = g_value_get_int (value);
+		changed = TRUE;
+		break;
+	case CHART_PROP_Y_POS:
+		chart->y_pos = g_value_get_int (value);
+		changed = TRUE;
+		break;
+	case CHART_PROP_COLUMNS:
+		chart->cols = g_value_get_int (value);
+		changed = TRUE;
+		break;
+	case CHART_PROP_ROWS:
+		chart->rows = g_value_get_int (value);
+		changed = TRUE;
+		break;
 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
 		 return; /* NOTE : RETURN */
 	}
+
+	if (changed) {
+		gog_graph_validate_chart_layout (GOG_GRAPH (GOG_OBJECT (chart)->parent));
+		gog_object_emit_changed (GOG_OBJECT (obj), TRUE);
+	}
 }
 
 static void
@@ -178,6 +204,18 @@ gog_chart_get_property (GObject *obj, guint param_id,
 	case CHART_PROP_PLOT_AREA_IS_MANUAL:
 		g_value_set_boolean (value, chart->is_plot_area_manual);
 		break;
+	case CHART_PROP_X_POS:
+		g_value_set_int (value, chart->x_pos);
+		break;
+	case CHART_PROP_Y_POS:
+		g_value_set_int (value, chart->y_pos);
+		break;
+	case CHART_PROP_COLUMNS:
+		g_value_set_int (value, chart->cols);
+		break;
+	case CHART_PROP_ROWS:
+		g_value_set_int (value, chart->rows);
+		break;
 
 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
 		 break;
@@ -505,6 +543,23 @@ gog_chart_class_init (GogObjectClass *gog_klass)
 				      _("Is plot area manual"),
 				      FALSE,
 				      GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+	g_object_class_install_property (gobject_klass, CHART_PROP_X_POS,
+		g_param_spec_int ("xpos", _("xpos"),
+			_("Horizontal chart position in graph grid"),
+			0, G_MAXINT, 0, G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+	/* we need to force saving of ypos since the default is not constant */
+	g_object_class_install_property (gobject_klass, CHART_PROP_Y_POS,
+		g_param_spec_int ("ypos", _("ypos"),
+			_("Vertical chart position in graph grid"),
+			0, G_MAXINT, 0, G_PARAM_READWRITE | GO_PARAM_PERSISTENT | GOG_PARAM_FORCE_SAVE));
+	g_object_class_install_property (gobject_klass, CHART_PROP_COLUMNS,
+		g_param_spec_int ("columns", _("columns"),
+			_("Number of columns in graph grid"),
+			1, G_MAXINT, 1, G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+	g_object_class_install_property (gobject_klass, CHART_PROP_ROWS,
+		g_param_spec_int ("rows", _("rows"),
+			_("Number of rows in graph grid"),
+			1, G_MAXINT, 1, G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
 
 	gog_klass->view_type = gog_chart_view_get_type ();
 	gog_klass->update    = gog_chart_update;
@@ -515,10 +570,12 @@ gog_chart_class_init (GogObjectClass *gog_klass)
 static void
 gog_chart_init (GogChart *chart)
 {
-	chart->x     = 0;
-	chart->y     = 0;
-	chart->cols  = 0;
-	chart->rows  = 0;
+	chart->x_pos =
+	chart->y_pos = 
+	chart->cols  = 		
+	chart->rows  =
+	chart->x_pos_actual = 
+	chart->y_pos_actual = 0;
 
 	/* start as true so that we can queue an update when it changes */
 	chart->cardinality_valid = TRUE;
@@ -554,8 +611,8 @@ gog_chart_get_position (GogChart const *chart,
 	if (chart->cols <= 0 || chart->rows <= 0)
 		return FALSE;
 
-	if (x != NULL)	  *x	= chart->x;
-	if (y != NULL)	  *y	= chart->y;
+	if (x != NULL)	  *x	= chart->x_pos;
+	if (y != NULL)	  *y	= chart->y_pos;
 	if (cols != NULL) *cols	= chart->cols;
 	if (rows != NULL) *rows	= chart->rows;
 
@@ -577,12 +634,12 @@ gog_chart_set_position (GogChart *chart,
 {
 	g_return_if_fail (GOG_IS_CHART (chart));
 
-	if (chart->x == x && chart->y == y &&
+	if (chart->x_pos == x && chart->y_pos == y &&
 	    chart->cols == cols && chart->rows == rows)
 		return;
 
-	chart->x = x;
-	chart->y = y;
+	chart->x_pos = x;
+	chart->y_pos = y;
 	chart->cols = cols;
 	chart->rows = rows;
 
diff --git a/goffice/graph/gog-graph.c b/goffice/graph/gog-graph.c
index d090159..44e7734 100644
--- a/goffice/graph/gog-graph.c
+++ b/goffice/graph/gog-graph.c
@@ -246,9 +246,24 @@ static void
 role_chart_post_add (GogObject *parent, GogObject *chart)
 {
 	GogGraph *graph = GOG_GRAPH (parent);
+	unsigned ypos = 0;
+	if (graph->charts != NULL) {
+		/* find the first row with an unoccupied first column */
+		gboolean *occ = g_alloca (graph->num_rows * sizeof (gboolean));
+		GSList *ptr;
+		int col, row;
+		memset (occ, 0, graph->num_rows * sizeof (gboolean));
+		for (ptr = graph->charts; ptr != NULL; ptr = ptr->next)
+			if (gog_chart_get_position (GOG_CHART (ptr->data), &col, &row, NULL, NULL) && col == 0 && row < (int) graph->num_rows)
+				occ[row] = TRUE;
+		while (ypos < graph->num_rows && occ[ypos])
+			ypos++;
+	}
 	graph->charts = g_slist_prepend (graph->charts, chart);
-	gog_chart_set_position (GOG_CHART (chart),
-		0, GOG_GRAPH (graph)->num_rows, 1, 1);
+	if (!gog_chart_get_position (GOG_CHART (chart), NULL, NULL, NULL, NULL))
+		/* search the first row with column 0 unocupied */
+		gog_chart_set_position (GOG_CHART (chart),
+			0, ypos, 1, 1);
 }
 
 static void
@@ -419,17 +434,19 @@ gog_graph_validate_chart_layout (GogGraph *graph)
 	max_col = max_row = 0;
 	for (ptr = graph->charts ; ptr != NULL ; ptr = ptr->next) {
 		chart = ptr->data;
-		if (max_col < (chart->x + chart->cols))
-			max_col = (chart->x + chart->cols);
-		if (max_row < (chart->y + chart->rows))
-			max_row = (chart->y + chart->rows);
+		chart->x_pos_actual = chart->x_pos;
+		chart->y_pos_actual = chart->y_pos;
+		if (max_col < (chart->x_pos_actual + chart->cols))
+			max_col = (chart->x_pos_actual + chart->cols);
+		if (max_row < (chart->y_pos_actual + chart->rows))
+			max_row = (chart->y_pos_actual + chart->rows);
 	}
 
 	/* 2) see if we need to contract any cols */
 	for (i = 0 ; i < max_col ; ) {
 		for (ptr = graph->charts ; ptr != NULL ; ptr = ptr->next) {
 			chart = ptr->data;
-			if (chart->x <= i && i < (chart->x + chart->cols))
+			if (chart->x_pos_actual <= i && i < (chart->x_pos_actual + chart->cols))
 				break;
 		}
 		if (ptr == NULL) {
@@ -437,18 +454,18 @@ gog_graph_validate_chart_layout (GogGraph *graph)
 			max_col--;
 			for (ptr = graph->charts ; ptr != NULL ; ptr = ptr->next) {
 				chart = ptr->data;
-				if (chart->x > i)
-					(chart->x)--;
+				if (chart->x_pos_actual > i)
+					(chart->x_pos_actual)--;
 			}
 		} else
-			i = chart->x + chart->cols;
+			i = chart->x_pos_actual + chart->cols;
 	}
 
 	/* 3) see if we need to contract any rows */
 	for (i = 0 ; i < max_row ; ) {
 		for (ptr = graph->charts ; ptr != NULL ; ptr = ptr->next) {
 			chart = ptr->data;
-			if (chart->y <= i && i < (chart->y + chart->rows))
+			if (chart->y_pos_actual <= i && i < (chart->y_pos_actual + chart->rows))
 				break;
 		}
 		if (ptr == NULL) {
@@ -456,11 +473,11 @@ gog_graph_validate_chart_layout (GogGraph *graph)
 			max_row--;
 			for (ptr = graph->charts ; ptr != NULL ; ptr = ptr->next) {
 				chart = ptr->data;
-				if (chart->y > i)
-					(chart->y)--;
+				if (chart->y_pos_actual > i)
+					(chart->y_pos_actual)--;
 			}
 		} else
-			i = chart->y + chart->rows;
+			i = chart->y_pos_actual + chart->rows;
 	}
 	changed |= (graph->num_cols != max_col || graph->num_rows != max_row);
 
@@ -797,7 +814,6 @@ gog_graph_view_size_allocate (GogView *view, GogViewAllocation const *bbox)
 {
 	GSList *ptr;
 	double h, w;
-	unsigned x, y, rows, cols;
 	GogView *child;
 	GogGraph *graph = GOG_GRAPH (view->model);
 	GogViewAllocation tmp, res = *bbox;
@@ -814,12 +830,11 @@ gog_graph_view_size_allocate (GogView *view, GogViewAllocation const *bbox)
 	for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
 		child = ptr->data;
 		if (GOG_POSITION_IS_SPECIAL (child->model->position)) {
-			gog_chart_get_position (GOG_CHART (child->model),
-				&x, &y, &cols, &rows);
-			tmp.x = x * w + res.x;
-			tmp.y = y * h + res.y;
-			tmp.w = cols * w;
-			tmp.h = rows * h;
+			GogChart *chart = GOG_CHART (child->model);
+			tmp.x = chart->x_pos_actual * w + res.x;
+			tmp.y = chart->y_pos_actual * h + res.y;
+			tmp.w = chart->cols * w;
+			tmp.h = chart->rows * h;
 			gog_view_size_allocate (child, &tmp);
 		}
 	}
diff --git a/goffice/graph/gog-object-prefs.ui b/goffice/graph/gog-object-prefs.ui
index a3e6062..0890442 100644
--- a/goffice/graph/gog-object-prefs.ui
+++ b/goffice/graph/gog-object-prefs.ui
@@ -1,5 +1,6 @@
 <?xml version="1.0"?>
 <interface>
+  <!-- interface-requires gtk+ 2.12 -->
   <!-- interface-naming-policy toplevel-contextual -->
   <object class="GtkAdjustment" id="adjustment1">
     <property name="lower">-10000</property>
@@ -426,6 +427,143 @@
             <property name="tab_fill">False</property>
           </packing>
         </child>
+        <child>
+          <object class="GtkTable" id="chart-table">
+            <property name="visible">True</property>
+            <property name="n_rows">2</property>
+            <property name="n_columns">4</property>
+            <property name="column_spacing">12</property>
+            <property name="row_spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="col-lbl">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Column:</property>
+              </object>
+              <packing>
+                <property name="x_options"></property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="row-lbl">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Row:</property>
+              </object>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="col-span-lbl">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="xpad">12</property>
+                <property name="label" translatable="yes">Width in columns:</property>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="row-span-lbl">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="xpad">12</property>
+                <property name="label" translatable="yes">Height in rows:</property>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSpinButton" id="xpos">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+                <property name="adjustment">col-adj</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSpinButton" id="ypos">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+                <property name="adjustment">row-adj</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSpinButton" id="columns">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+                <property name="adjustment">col-span-adj</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSpinButton" id="rows">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+                <property name="adjustment">row-span-adj</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+                <property name="x_options">GTK_FILL</property>
+                <property name="y_options"></property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="label1">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">page 3</property>
+          </object>
+          <packing>
+            <property name="position">2</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
       </object>
       <packing>
         <property name="position">1</property>
@@ -450,4 +588,28 @@
       <column type="gchararray"/>
     </columns>
   </object>
+  <object class="GtkAdjustment" id="row-adj">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10.01</property>
+  </object>
+  <object class="GtkAdjustment" id="row-span-adj">
+    <property name="value">1</property>
+    <property name="lower">1</property>
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="col-adj">
+    <property name="upper">10000</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="col-span-adj">
+    <property name="value">1</property>
+    <property name="lower">1</property>
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
 </interface>
diff --git a/goffice/graph/gog-object.c b/goffice/graph/gog-object.c
index e10f130..2d7f3c1 100644
--- a/goffice/graph/gog-object.c
+++ b/goffice/graph/gog-object.c
@@ -318,6 +318,8 @@ update_select_state (ObjectPrefState *state)
 		int index = gog_object_get_position_flags (state->gobj, GOG_POSITION_MANUAL) == 0 ? 0 : 1;
 
 		gtk_combo_box_set_active (GTK_COMBO_BOX (state->position_select_combo), index);
+		if (index == 0 && GOG_IS_CHART (state->gobj))
+			index = 2;
 		gtk_notebook_set_current_page (GTK_NOTEBOOK (state->position_notebook), index);
 	}
 }
@@ -330,6 +332,8 @@ cb_manual_position_changed (GtkComboBox *combo, ObjectPrefState *state)
 	gog_object_set_position_flags (state->gobj,
 		index != 0 ? GOG_POSITION_MANUAL : 0,
 		GOG_POSITION_MANUAL);
+	if (index == 0 && GOG_IS_CHART (state->gobj))
+		index = 2;
 	gtk_notebook_set_current_page (GTK_NOTEBOOK (state->position_notebook), index);
 }
 
@@ -357,6 +361,13 @@ cb_update_editor (GogObject *gobj, ObjectPrefState *state)
 }
 
 static void
+cb_chart_position_changed (GtkWidget *spin, ObjectPrefState *state)
+{
+	g_object_set (G_OBJECT (state->gobj), gtk_widget_get_name (spin),
+		      (int) gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin)), NULL);
+}
+
+static void
 gog_object_populate_editor (GogObject *gobj,
 			    GOEditor *editor,
 			    G_GNUC_UNUSED GogDataAllocator *dalloc,
@@ -492,8 +503,28 @@ gog_object_populate_editor (GogObject *gobj,
 			w = go_gtk_builder_get_widget (gui, "manual_sizes");
 			gtk_widget_hide (w);
 		}
-	} else
-		gtk_notebook_set_current_page (GTK_NOTEBOOK (state->position_notebook), 0);
+	}
+	if (GOG_IS_CHART (gobj)) {
+		/* setting special notebook page */
+		int col, row, cols, rows;
+		g_object_get (G_OBJECT (gobj), "xpos", &col, "ypos", &row, "columns", &cols, "rows", &rows, NULL);
+		w = go_gtk_builder_get_widget (gui, "xpos");
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), col);
+		g_signal_connect (G_OBJECT (w), "value-changed",
+				  G_CALLBACK (cb_chart_position_changed), state);
+		w = go_gtk_builder_get_widget (gui, "columns");
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), cols);
+		g_signal_connect (G_OBJECT (w), "value-changed",
+				  G_CALLBACK (cb_chart_position_changed), state);
+		w = go_gtk_builder_get_widget (gui, "ypos");
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), row);
+		g_signal_connect (G_OBJECT (w), "value-changed",
+				  G_CALLBACK (cb_chart_position_changed), state);
+		w = go_gtk_builder_get_widget (gui, "rows");
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), rows);
+		g_signal_connect (G_OBJECT (w), "value-changed",
+				  G_CALLBACK (cb_chart_position_changed), state);
+	}
 
 	g_object_unref (G_OBJECT (widget_size_group));
 	g_object_unref (G_OBJECT (label_size_group));



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