GUI: Dead kittens.
[gnumeric.git] / src / gnm-sheet-slicer.c
blob56fb4674fbbd7bf0f890ae49f619f00e766f9b3f
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * gnm-sheet-slicer.c:
5 * Copyright (C) 2008-2009 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
23 #include <gnumeric-config.h>
24 #include "gnm-sheet-slicer.h"
25 #include "go-data-slicer-impl.h"
26 #include "go-data-slicer-field-impl.h"
27 #include "go-data-cache.h"
28 #include "sheet.h"
29 #include "ranges.h"
31 #include <gsf/gsf-impl-utils.h>
32 #include <glib/gi18n-lib.h>
33 #include <string.h>
35 #include <glib-object.h>
37 struct _GnmSheetSlicer {
38 GODataSlicer base;
40 Sheet *sheet;
41 GnmRange range;
43 /* Offsets from the top-left (in LTR) pos range */
44 unsigned int first_header_row, first_data_row, first_data_col;
45 unsigned int row_page_count, col_page_count;
47 struct {
48 gboolean headers_col, headers_row, stripes_col, stripes_row, last_col, last_row;
49 } show;
51 GnmSheetSlicerLayout layout;
53 typedef GODataSlicerClass GnmSheetSlicerClass;
55 #define GNM_SHEET_SLICER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SHEET_SLICER_TYPE, GnmSheetSlicerClass))
56 #define GNM_IS_SHEET_SLICER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GNM_SHEET_SLICER_TYPE))
57 #define GNM_SHEET_SLICER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GNM_SHEET_SLICER_TYPE, GnmSheetSlicerClass))
59 enum {
60 PROP_0,
61 PROP_SHEET,
62 PROP_RANGE,
64 PROP_FIRST_HEADER_ROW,
65 PROP_FIRST_DATA_COL,
66 PROP_FIRST_DATA_ROW,
68 PROP_SHOW_HEADERS_COL,
69 PROP_SHOW_HEADERS_ROW,
70 PROP_SHOW_STRIPES_COL,
71 PROP_SHOW_STRIPES_ROW,
72 PROP_SHOW_LAST_COL,
73 PROP_SHOW_LAST_ROW,
75 PROP_LAYOUT
78 static GObjectClass *parent_klass;
79 static void
80 gnm_sheet_slicer_init (GnmSheetSlicer *gss)
82 gss->sheet = NULL;
83 gss->first_header_row = gss->first_data_row = gss->first_data_col = gss->row_page_count = gss->col_page_count = 0;
86 static void
87 gnm_sheet_slicer_finalize (GObject *obj)
89 GnmSheetSlicer *gss = (GnmSheetSlicer *)obj;
91 if (NULL != gss->sheet) {
92 g_warning ("finalizing a slicer that is still attached to a sheet");
95 (parent_klass->finalize) (obj);
98 static void
99 gnm_sheet_slicer_set_property (GObject *obj, guint property_id,
100 GValue const *value, GParamSpec *pspec)
102 GnmSheetSlicer *gss = (GnmSheetSlicer *)obj;
104 switch (property_id) {
105 case PROP_SHEET : gnm_sheet_slicer_set_sheet (gss, g_value_get_object (value)); break;
106 case PROP_RANGE : gnm_sheet_slicer_set_range (gss, g_value_get_boxed (value)); break;
107 case PROP_FIRST_HEADER_ROW : gss->first_header_row = g_value_get_uint (value); break;
108 case PROP_FIRST_DATA_COL : gss->first_data_col = g_value_get_uint (value); break;
109 case PROP_FIRST_DATA_ROW : gss->first_data_row = g_value_get_uint (value); break;
111 case PROP_SHOW_HEADERS_COL : gss->show.headers_col = g_value_get_boolean (value); break;
112 case PROP_SHOW_HEADERS_ROW : gss->show.headers_row = g_value_get_boolean (value); break;
113 case PROP_SHOW_STRIPES_COL : gss->show.stripes_col = g_value_get_boolean (value); break;
114 case PROP_SHOW_STRIPES_ROW : gss->show.stripes_row = g_value_get_boolean (value); break;
115 case PROP_SHOW_LAST_COL : gss->show.last_col = g_value_get_boolean (value); break;
116 case PROP_SHOW_LAST_ROW : gss->show.last_row = g_value_get_boolean (value); break;
118 case PROP_LAYOUT: gnm_sheet_slicer_set_layout (gss, g_value_get_enum (value)); break;
119 default:
120 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
124 static void
125 gnm_sheet_slicer_get_property (GObject *obj, guint property_id,
126 GValue *value, GParamSpec *pspec)
128 GnmSheetSlicer const *gss = (GnmSheetSlicer const *)obj;
129 switch (property_id) {
130 case PROP_SHEET : g_value_set_object (value, gss->sheet); break;
131 case PROP_RANGE : g_value_set_boxed (value, &gss->range); break;
132 case PROP_FIRST_HEADER_ROW : g_value_set_uint (value, gss->first_header_row ); break;
133 case PROP_FIRST_DATA_COL : g_value_set_uint (value, gss->first_data_col); break;
134 case PROP_FIRST_DATA_ROW : g_value_set_uint (value, gss->first_data_row); break;
136 case PROP_SHOW_HEADERS_COL : g_value_set_boolean (value, gss->show.headers_col); break;
137 case PROP_SHOW_HEADERS_ROW : g_value_set_boolean (value, gss->show.headers_row); break;
138 case PROP_SHOW_STRIPES_COL : g_value_set_boolean (value, gss->show.stripes_col); break;
139 case PROP_SHOW_STRIPES_ROW : g_value_set_boolean (value, gss->show.stripes_row); break;
140 case PROP_SHOW_LAST_COL : g_value_set_boolean (value, gss->show.last_col); break;
141 case PROP_SHOW_LAST_ROW : g_value_set_boolean (value, gss->show.last_row); break;
143 case PROP_LAYOUT : g_value_set_enum (value, gss->layout); break;
144 default:
145 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
149 static void
150 gnm_sheet_slicer_class_init (GnmSheetSlicerClass *klass)
152 GObjectClass *gobject_class = (GObjectClass *)klass;
153 gobject_class->set_property = gnm_sheet_slicer_set_property;
154 gobject_class->get_property = gnm_sheet_slicer_get_property;
155 gobject_class->finalize = gnm_sheet_slicer_finalize;
157 g_object_class_install_property (gobject_class, PROP_SHEET,
158 g_param_spec_object ("sheet", NULL, NULL, GNM_SHEET_TYPE,
159 GSF_PARAM_STATIC | G_PARAM_READWRITE));
160 g_object_class_install_property (gobject_class, PROP_RANGE,
161 g_param_spec_boxed ("range", NULL, NULL, gnm_range_get_type (),
162 GSF_PARAM_STATIC | G_PARAM_READWRITE));
163 g_object_class_install_property (gobject_class, PROP_FIRST_HEADER_ROW,
164 g_param_spec_uint ("first-header-row", NULL, NULL, 0, GNM_MAX_ROWS, 0,
165 GSF_PARAM_STATIC | G_PARAM_READWRITE));
166 g_object_class_install_property (gobject_class, PROP_FIRST_DATA_COL,
167 g_param_spec_uint ("first-data-col", NULL, NULL, 0, GNM_MAX_COLS, 0,
168 GSF_PARAM_STATIC | G_PARAM_READWRITE));
169 g_object_class_install_property (gobject_class, PROP_FIRST_DATA_ROW,
170 g_param_spec_uint ("first-data-row", NULL, NULL, 0, GNM_MAX_ROWS, 0,
171 GSF_PARAM_STATIC | G_PARAM_READWRITE));
173 g_object_class_install_property (gobject_class, PROP_SHOW_HEADERS_COL,
174 g_param_spec_boolean ("show-headers-col", NULL, NULL, TRUE,
175 GSF_PARAM_STATIC | G_PARAM_READWRITE));
176 g_object_class_install_property (gobject_class, PROP_SHOW_HEADERS_ROW,
177 g_param_spec_boolean ("show-headers-row", NULL, NULL, TRUE,
178 GSF_PARAM_STATIC | G_PARAM_READWRITE));
179 g_object_class_install_property (gobject_class, PROP_SHOW_STRIPES_COL,
180 g_param_spec_boolean ("show-stripes-col", NULL, NULL, TRUE,
181 GSF_PARAM_STATIC | G_PARAM_READWRITE));
182 g_object_class_install_property (gobject_class, PROP_SHOW_STRIPES_ROW,
183 g_param_spec_boolean ("show-stripes-row", NULL, NULL, TRUE,
184 GSF_PARAM_STATIC | G_PARAM_READWRITE));
185 g_object_class_install_property (gobject_class, PROP_SHOW_LAST_COL,
186 g_param_spec_boolean ("show-last-col", NULL, NULL, TRUE,
187 GSF_PARAM_STATIC | G_PARAM_READWRITE));
188 g_object_class_install_property (gobject_class, PROP_SHOW_LAST_ROW,
189 g_param_spec_boolean ("show-last-row", NULL, NULL, TRUE,
190 GSF_PARAM_STATIC | G_PARAM_READWRITE));
192 g_object_class_install_property (gobject_class, PROP_LAYOUT,
193 g_param_spec_enum ("layout", NULL, NULL, gnm_sheet_slicer_layout_get_type (), GSS_LAYOUT_XL_OUTLINE,
194 GSF_PARAM_STATIC | G_PARAM_READWRITE));
195 parent_klass = g_type_class_peek_parent (klass);
198 GSF_CLASS (GnmSheetSlicer, gnm_sheet_slicer,
199 gnm_sheet_slicer_class_init, gnm_sheet_slicer_init,
200 GO_DATA_SLICER_TYPE)
202 void
203 gnm_sheet_slicer_set_sheet (GnmSheetSlicer *gss, Sheet *sheet)
205 g_return_if_fail (IS_SHEET (sheet));
206 g_return_if_fail (GNM_IS_SHEET_SLICER (gss));
207 g_return_if_fail (NULL == gss->sheet);
209 g_object_ref (gss);
210 gss->sheet = sheet;
211 sheet->slicers = g_slist_prepend (sheet->slicers, gss);
214 void
215 gnm_sheet_slicer_clear_sheet (GnmSheetSlicer *gss)
217 g_return_if_fail (GNM_IS_SHEET_SLICER (gss));
218 g_return_if_fail (NULL != gss->sheet);
220 gss->sheet->slicers = g_slist_remove (gss->sheet->slicers, gss);
221 gss->sheet = NULL;
222 g_object_unref (gss);
225 GnmRange const *
226 gnm_sheet_slicer_get_range (GnmSheetSlicer const *gss)
228 g_return_val_if_fail (GNM_IS_SHEET_SLICER (gss), NULL);
229 return &gss->range;
232 void
233 gnm_sheet_slicer_set_range (GnmSheetSlicer *gss, GnmRange const *r)
235 g_return_if_fail (GNM_IS_SHEET_SLICER (gss));
236 gss->range = *r;
240 * gnm_sheet_slicer_overlaps_range:
241 * @gss: #GnmSheetSlicer
242 * @r: #GnmRange
244 * Returns: %TRUE if @gss overlaps @r.
246 gboolean
247 gnm_sheet_slicer_overlaps_range (GnmSheetSlicer const *gss, GnmRange const *r)
249 g_return_val_if_fail (GNM_IS_SHEET_SLICER (gss), FALSE);
250 return range_overlap (&gss->range, r);
254 * gnm_sheet_slicer_field_header_at_pos:
255 * @gss: #GnmSheetSlicer const
256 * @pos: #GnmCellPos const
258 * Checks to see if @pos (in absolute position, not relative to @gss' corner)
259 * corresponds to a field header. [Does not add a reference]
261 * Returns a #GODataSlicerField or %NULL.
263 GODataSlicerField *
264 gnm_sheet_slicer_field_header_at_pos (GnmSheetSlicer const *gss,
265 GnmCellPos const *pos)
267 int res = -1;
268 unsigned int c, r;
270 g_return_val_if_fail (GNM_IS_SHEET_SLICER (gss), NULL);
272 /* 0) TODO page fields */
273 if (pos->col < gss->range.start.col || pos->row < gss->range.start.row)
274 return NULL;
276 c = pos->col - gss->range.start.col;
277 r = pos->row - gss->range.start.row;
279 /* TODO other layouts */
281 /* col headers along the top starting at first_data_col */
282 if (r == 0 &&
283 c >= gss->first_data_col) {
284 c -= gss->first_data_col;
285 if (c < gss->base.fields[GDS_FIELD_TYPE_COL]->len)
286 res = g_array_index (gss->base.fields[GDS_FIELD_TYPE_COL], int, c);
289 /* row headers just about data starting at 0th col */
290 } else if (r >= (gss->first_data_row - 1) && /* -1 for the headers */
291 c < gss->first_data_col) {
292 if (c < gss->base.fields[GDS_FIELD_TYPE_ROW]->len)
293 res = g_array_index (gss->base.fields[GDS_FIELD_TYPE_ROW], int, c);
296 return (res >= 0) ? go_data_slicer_get_field (&gss->base, res) : NULL;
299 /************************************************************/
302 * gnm_sheet_slicers_at_pos:
303 * @sheet: #Sheet
304 * @pos: #GnmCellPos
306 * Returns: (transfer none): %NULL or the #GnmSheetSlicer in @sheet that overlaps with @pos.
308 GnmSheetSlicer *
309 gnm_sheet_slicers_at_pos (Sheet const *sheet, GnmCellPos const *pos)
311 GSList *ptr;
312 GnmRange r;
314 g_return_val_if_fail (IS_SHEET (sheet), NULL);
315 g_return_val_if_fail (NULL != pos, NULL);
317 range_init_cellpos (&r, pos);
318 for (ptr = sheet->slicers; ptr != NULL ; ptr = ptr->next)
319 if (gnm_sheet_slicer_overlaps_range (ptr->data, &r))
320 return ptr->data;
322 return NULL;
325 #if 0
326 static void
327 gss_append_field_indicies (GnmSheetSlicer const *gss, GODataSlicerFieldType type,
328 GArray *field_order)
330 GArray *tmp = gss->base.fields [type];
331 unsigned int i, n = tmp->len;
332 for (i = 0 ; i < n; i++)
333 g_array_append_val (field_order, g_array_index (tmp, int, i));
336 static void
337 gnm_sheet_slicer_test_sort (GnmSheetSlicer *gss)
339 /* quick test to sort the cache based on the row/col */
340 GArray *permutation, *field_order;
341 unsigned int i, n;
343 field_order = g_array_sized_new (FALSE, FALSE, sizeof (unsigned int), gss->base.all_fields->len);
344 gss_append_field_indicies (gss, GDS_FIELD_TYPE_ROW, field_order);
345 gss_append_field_indicies (gss, GDS_FIELD_TYPE_COL, field_order);
347 n = go_data_cache_num_items (gss->base.cache);
348 permutation = g_array_sized_new (FALSE, FALSE, sizeof (int), n);
349 for (i = 0 ; i < n ; i++)
350 g_array_append_val (permutation, i);
351 go_data_cache_permute (gss->base.cache, field_order, permutation);
352 go_data_cache_dump (gss->base.cache, field_order, permutation);
354 g_array_free (field_order, TRUE);
355 g_array_free (permutation, TRUE);
357 #endif
360 * gnm_sheet_slicer_regenerate:
361 * @gss: #GnmSheetSlicer
363 * Do some work!
364 * See what we need to do then think about when portions belong in the GODataSlicer base.
367 void
368 gnm_sheet_slicer_regenerate (GnmSheetSlicer *gss)
370 #if 0
371 GArray *permutation, *rows;
372 unsigned int i, n;
374 g_return_if_fail (GNM_IS_SHEET_SLICER (gss));
375 g_return_if_fail (IS_SHEET (gss->sheet));
376 g_return_if_fail (NULL != gss->base.cache);
378 field_order = g_array_sized_new (FALSE, FALSE, sizeof (unsigned int), gss->base.all_fields->len);
379 gss_append_field_indicies (gss, GDS_FIELD_TYPE_ROW, field_order);
380 gss_append_field_indicies (gss, GDS_FIELD_TYPE_COL, field_order);
382 n = go_data_cache_num_items (gss->base.cache);
383 #endif
386 GnmSheetSlicerLayout
387 gnm_sheet_slicer_get_layout (GnmSheetSlicer const *gss)
389 g_return_val_if_fail (GNM_IS_SHEET_SLICER (gss), GSS_LAYOUT_XL_OUTLINE);
390 return gss->layout;
393 void
394 gnm_sheet_slicer_set_layout (GnmSheetSlicer *gss, GnmSheetSlicerLayout l)
396 g_return_if_fail (GNM_IS_SHEET_SLICER (gss));
397 gss->layout = l;
400 GType
401 gnm_sheet_slicer_layout_get_type (void)
403 static GType etype = 0;
404 if (etype == 0) {
405 static GEnumValue const values[] = {
406 { GSS_LAYOUT_XL_OUTLINE, "GSS_LAYOUT_XL_OUTLINE", "xl-outline" },
407 { GSS_LAYOUT_XL_COMPACT, "GSS_LAYOUT_XL_COMPACT", "xl-compact" },
408 { GSS_LAYOUT_XL_TABULAR, "GSS_LAYOUT_XL_TABULAR", "xl-tabular" },
409 { 0, NULL, NULL }
411 etype = g_enum_register_static ("GnmSheetSlicerLayout", values);
413 return etype;