1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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
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"
31 #include <gsf/gsf-impl-utils.h>
32 #include <glib/gi18n-lib.h>
35 #include <glib-object.h>
37 struct _GnmSheetSlicer
{
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
;
48 gboolean headers_col
, headers_row
, stripes_col
, stripes_row
, last_col
, last_row
;
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))
64 PROP_FIRST_HEADER_ROW
,
68 PROP_SHOW_HEADERS_COL
,
69 PROP_SHOW_HEADERS_ROW
,
70 PROP_SHOW_STRIPES_COL
,
71 PROP_SHOW_STRIPES_ROW
,
78 static GObjectClass
*parent_klass
;
80 gnm_sheet_slicer_init (GnmSheetSlicer
*gss
)
83 gss
->first_header_row
= gss
->first_data_row
= gss
->first_data_col
= gss
->row_page_count
= gss
->col_page_count
= 0;
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
);
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;
120 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, property_id
, pspec
);
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;
145 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, property_id
, pspec
);
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
,
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
);
211 sheet
->slicers
= g_slist_prepend (sheet
->slicers
, gss
);
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
);
222 g_object_unref (gss
);
226 gnm_sheet_slicer_get_range (GnmSheetSlicer
const *gss
)
228 g_return_val_if_fail (GNM_IS_SHEET_SLICER (gss
), NULL
);
233 gnm_sheet_slicer_set_range (GnmSheetSlicer
*gss
, GnmRange
const *r
)
235 g_return_if_fail (GNM_IS_SHEET_SLICER (gss
));
240 * gnm_sheet_slicer_overlaps_range :
241 * @gss: #GnmSheetSlicer
244 * Returns: %TRUE if @gss overlaps @r.
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.
264 gnm_sheet_slicer_field_header_at_pos (GnmSheetSlicer
const *gss
,
265 GnmCellPos
const *pos
)
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
)
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 */
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:
306 * Returns: (transfer none): %NULL or the #GnmSheetSlicer in @sheet that overlaps with @pos.
309 gnm_sheet_slicers_at_pos (Sheet
const *sheet
, GnmCellPos
const *pos
)
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
))
327 gss_append_field_indicies (GnmSheetSlicer
const *gss
, GODataSlicerFieldType type
,
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
));
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
;
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
);
360 * gnm_sheet_slicer_regenerate:
361 * @gss: #GnmSheetSlicer
364 * See what we need to do then think about when portions belong in the GODataSlicer base.
368 gnm_sheet_slicer_regenerate (GnmSheetSlicer
*gss
)
371 GArray
*permutation
, *rows
;
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
);
387 gnm_sheet_slicer_get_layout (GnmSheetSlicer
const *gss
)
389 g_return_val_if_fail (GNM_IS_SHEET_SLICER (gss
), GSS_LAYOUT_XL_OUTLINE
);
394 gnm_sheet_slicer_set_layout (GnmSheetSlicer
*gss
, GnmSheetSlicerLayout l
)
396 g_return_if_fail (GNM_IS_SHEET_SLICER (gss
));
401 gnm_sheet_slicer_layout_get_type (void)
403 static GType 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" },
411 etype
= g_enum_register_static ("GnmSheetSlicerLayout", values
);