4 * Copyright (C) 2008-2009 Jody Goldberg (jody@gnome.org)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 #include <gnumeric-config.h>
23 #include <gnm-sheet-slicer.h>
24 #include <go-data-slicer-impl.h>
25 #include <go-data-slicer-field-impl.h>
26 #include <go-data-cache.h>
30 #include <gsf/gsf-impl-utils.h>
31 #include <glib/gi18n-lib.h>
34 #include <glib-object.h>
36 struct _GnmSheetSlicer
{
42 /* Offsets from the top-left (in LTR) pos range */
43 unsigned int first_header_row
, first_data_row
, first_data_col
;
44 unsigned int row_page_count
, col_page_count
;
47 gboolean headers_col
, headers_row
, stripes_col
, stripes_row
, last_col
, last_row
;
50 GnmSheetSlicerLayout layout
;
52 typedef GODataSlicerClass GnmSheetSlicerClass
;
54 #define GNM_SHEET_SLICER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SHEET_SLICER_TYPE, GnmSheetSlicerClass))
55 #define GNM_IS_SHEET_SLICER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GNM_SHEET_SLICER_TYPE))
56 #define GNM_SHEET_SLICER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GNM_SHEET_SLICER_TYPE, GnmSheetSlicerClass))
63 PROP_FIRST_HEADER_ROW
,
67 PROP_SHOW_HEADERS_COL
,
68 PROP_SHOW_HEADERS_ROW
,
69 PROP_SHOW_STRIPES_COL
,
70 PROP_SHOW_STRIPES_ROW
,
77 static GObjectClass
*parent_klass
;
79 gnm_sheet_slicer_init (GnmSheetSlicer
*gss
)
82 gss
->first_header_row
= gss
->first_data_row
= gss
->first_data_col
= gss
->row_page_count
= gss
->col_page_count
= 0;
86 gnm_sheet_slicer_finalize (GObject
*obj
)
88 GnmSheetSlicer
*gss
= (GnmSheetSlicer
*)obj
;
90 if (NULL
!= gss
->sheet
) {
91 g_warning ("finalizing a slicer that is still attached to a sheet");
94 (parent_klass
->finalize
) (obj
);
98 gnm_sheet_slicer_set_property (GObject
*obj
, guint property_id
,
99 GValue
const *value
, GParamSpec
*pspec
)
101 GnmSheetSlicer
*gss
= (GnmSheetSlicer
*)obj
;
103 switch (property_id
) {
104 case PROP_SHEET
: gnm_sheet_slicer_set_sheet (gss
, g_value_get_object (value
)); break;
105 case PROP_RANGE
: gnm_sheet_slicer_set_range (gss
, g_value_get_boxed (value
)); break;
106 case PROP_FIRST_HEADER_ROW
: gss
->first_header_row
= g_value_get_uint (value
); break;
107 case PROP_FIRST_DATA_COL
: gss
->first_data_col
= g_value_get_uint (value
); break;
108 case PROP_FIRST_DATA_ROW
: gss
->first_data_row
= g_value_get_uint (value
); break;
110 case PROP_SHOW_HEADERS_COL
: gss
->show
.headers_col
= g_value_get_boolean (value
); break;
111 case PROP_SHOW_HEADERS_ROW
: gss
->show
.headers_row
= g_value_get_boolean (value
); break;
112 case PROP_SHOW_STRIPES_COL
: gss
->show
.stripes_col
= g_value_get_boolean (value
); break;
113 case PROP_SHOW_STRIPES_ROW
: gss
->show
.stripes_row
= g_value_get_boolean (value
); break;
114 case PROP_SHOW_LAST_COL
: gss
->show
.last_col
= g_value_get_boolean (value
); break;
115 case PROP_SHOW_LAST_ROW
: gss
->show
.last_row
= g_value_get_boolean (value
); break;
117 case PROP_LAYOUT
: gnm_sheet_slicer_set_layout (gss
, g_value_get_enum (value
)); break;
119 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, property_id
, pspec
);
124 gnm_sheet_slicer_get_property (GObject
*obj
, guint property_id
,
125 GValue
*value
, GParamSpec
*pspec
)
127 GnmSheetSlicer
const *gss
= (GnmSheetSlicer
const *)obj
;
128 switch (property_id
) {
129 case PROP_SHEET
: g_value_set_object (value
, gss
->sheet
); break;
130 case PROP_RANGE
: g_value_set_boxed (value
, &gss
->range
); break;
131 case PROP_FIRST_HEADER_ROW
: g_value_set_uint (value
, gss
->first_header_row
); break;
132 case PROP_FIRST_DATA_COL
: g_value_set_uint (value
, gss
->first_data_col
); break;
133 case PROP_FIRST_DATA_ROW
: g_value_set_uint (value
, gss
->first_data_row
); break;
135 case PROP_SHOW_HEADERS_COL
: g_value_set_boolean (value
, gss
->show
.headers_col
); break;
136 case PROP_SHOW_HEADERS_ROW
: g_value_set_boolean (value
, gss
->show
.headers_row
); break;
137 case PROP_SHOW_STRIPES_COL
: g_value_set_boolean (value
, gss
->show
.stripes_col
); break;
138 case PROP_SHOW_STRIPES_ROW
: g_value_set_boolean (value
, gss
->show
.stripes_row
); break;
139 case PROP_SHOW_LAST_COL
: g_value_set_boolean (value
, gss
->show
.last_col
); break;
140 case PROP_SHOW_LAST_ROW
: g_value_set_boolean (value
, gss
->show
.last_row
); break;
142 case PROP_LAYOUT
: g_value_set_enum (value
, gss
->layout
); break;
144 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, property_id
, pspec
);
149 gnm_sheet_slicer_class_init (GnmSheetSlicerClass
*klass
)
151 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
152 gobject_class
->set_property
= gnm_sheet_slicer_set_property
;
153 gobject_class
->get_property
= gnm_sheet_slicer_get_property
;
154 gobject_class
->finalize
= gnm_sheet_slicer_finalize
;
156 g_object_class_install_property (gobject_class
, PROP_SHEET
,
157 g_param_spec_object ("sheet", NULL
, NULL
, GNM_SHEET_TYPE
,
158 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
159 g_object_class_install_property (gobject_class
, PROP_RANGE
,
160 g_param_spec_boxed ("range", NULL
, NULL
, gnm_range_get_type (),
161 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
162 g_object_class_install_property (gobject_class
, PROP_FIRST_HEADER_ROW
,
163 g_param_spec_uint ("first-header-row", NULL
, NULL
, 0, GNM_MAX_ROWS
, 0,
164 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
165 g_object_class_install_property (gobject_class
, PROP_FIRST_DATA_COL
,
166 g_param_spec_uint ("first-data-col", NULL
, NULL
, 0, GNM_MAX_COLS
, 0,
167 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
168 g_object_class_install_property (gobject_class
, PROP_FIRST_DATA_ROW
,
169 g_param_spec_uint ("first-data-row", NULL
, NULL
, 0, GNM_MAX_ROWS
, 0,
170 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
172 g_object_class_install_property (gobject_class
, PROP_SHOW_HEADERS_COL
,
173 g_param_spec_boolean ("show-headers-col", NULL
, NULL
, TRUE
,
174 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
175 g_object_class_install_property (gobject_class
, PROP_SHOW_HEADERS_ROW
,
176 g_param_spec_boolean ("show-headers-row", NULL
, NULL
, TRUE
,
177 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
178 g_object_class_install_property (gobject_class
, PROP_SHOW_STRIPES_COL
,
179 g_param_spec_boolean ("show-stripes-col", NULL
, NULL
, TRUE
,
180 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
181 g_object_class_install_property (gobject_class
, PROP_SHOW_STRIPES_ROW
,
182 g_param_spec_boolean ("show-stripes-row", NULL
, NULL
, TRUE
,
183 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
184 g_object_class_install_property (gobject_class
, PROP_SHOW_LAST_COL
,
185 g_param_spec_boolean ("show-last-col", NULL
, NULL
, TRUE
,
186 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
187 g_object_class_install_property (gobject_class
, PROP_SHOW_LAST_ROW
,
188 g_param_spec_boolean ("show-last-row", NULL
, NULL
, TRUE
,
189 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
191 g_object_class_install_property (gobject_class
, PROP_LAYOUT
,
192 g_param_spec_enum ("layout", NULL
, NULL
, gnm_sheet_slicer_layout_get_type (), GSS_LAYOUT_XL_OUTLINE
,
193 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
194 parent_klass
= g_type_class_peek_parent (klass
);
197 GSF_CLASS (GnmSheetSlicer
, gnm_sheet_slicer
,
198 gnm_sheet_slicer_class_init
, gnm_sheet_slicer_init
,
202 gnm_sheet_slicer_set_sheet (GnmSheetSlicer
*gss
, Sheet
*sheet
)
204 g_return_if_fail (IS_SHEET (sheet
));
205 g_return_if_fail (GNM_IS_SHEET_SLICER (gss
));
206 g_return_if_fail (NULL
== gss
->sheet
);
210 sheet
->slicers
= g_slist_prepend (sheet
->slicers
, gss
);
214 gnm_sheet_slicer_clear_sheet (GnmSheetSlicer
*gss
)
216 g_return_if_fail (GNM_IS_SHEET_SLICER (gss
));
217 g_return_if_fail (NULL
!= gss
->sheet
);
219 gss
->sheet
->slicers
= g_slist_remove (gss
->sheet
->slicers
, gss
);
221 g_object_unref (gss
);
225 gnm_sheet_slicer_get_range (GnmSheetSlicer
const *gss
)
227 g_return_val_if_fail (GNM_IS_SHEET_SLICER (gss
), NULL
);
232 gnm_sheet_slicer_set_range (GnmSheetSlicer
*gss
, GnmRange
const *r
)
234 g_return_if_fail (GNM_IS_SHEET_SLICER (gss
));
239 * gnm_sheet_slicer_overlaps_range:
240 * @gss: #GnmSheetSlicer
243 * Returns: %TRUE if @gss overlaps @r.
246 gnm_sheet_slicer_overlaps_range (GnmSheetSlicer
const *gss
, GnmRange
const *r
)
248 g_return_val_if_fail (GNM_IS_SHEET_SLICER (gss
), FALSE
);
249 return range_overlap (&gss
->range
, r
);
253 * gnm_sheet_slicer_field_header_at_pos:
254 * @gss: #GnmSheetSlicer const
255 * @pos: #GnmCellPos const
257 * Checks to see if @pos (in absolute position, not relative to @gss' corner)
258 * corresponds to a field header. [Does not add a reference]
260 * Returns a #GODataSlicerField or %NULL.
263 gnm_sheet_slicer_field_header_at_pos (GnmSheetSlicer
const *gss
,
264 GnmCellPos
const *pos
)
269 g_return_val_if_fail (GNM_IS_SHEET_SLICER (gss
), NULL
);
271 /* 0) TODO page fields */
272 if (pos
->col
< gss
->range
.start
.col
|| pos
->row
< gss
->range
.start
.row
)
275 c
= pos
->col
- gss
->range
.start
.col
;
276 r
= pos
->row
- gss
->range
.start
.row
;
278 /* TODO other layouts */
280 /* col headers along the top starting at first_data_col */
282 c
>= gss
->first_data_col
) {
283 c
-= gss
->first_data_col
;
284 if (c
< gss
->base
.fields
[GDS_FIELD_TYPE_COL
]->len
)
285 res
= g_array_index (gss
->base
.fields
[GDS_FIELD_TYPE_COL
], int, c
);
288 /* row headers just about data starting at 0th col */
289 } else if (r
>= (gss
->first_data_row
- 1) && /* -1 for the headers */
290 c
< gss
->first_data_col
) {
291 if (c
< gss
->base
.fields
[GDS_FIELD_TYPE_ROW
]->len
)
292 res
= g_array_index (gss
->base
.fields
[GDS_FIELD_TYPE_ROW
], int, c
);
295 return (res
>= 0) ? go_data_slicer_get_field (&gss
->base
, res
) : NULL
;
298 /************************************************************/
301 * gnm_sheet_slicers_at_pos:
305 * Returns: (transfer none): %NULL or the #GnmSheetSlicer in @sheet that overlaps with @pos.
308 gnm_sheet_slicers_at_pos (Sheet
const *sheet
, GnmCellPos
const *pos
)
313 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
314 g_return_val_if_fail (NULL
!= pos
, NULL
);
316 range_init_cellpos (&r
, pos
);
317 for (ptr
= sheet
->slicers
; ptr
!= NULL
; ptr
= ptr
->next
)
318 if (gnm_sheet_slicer_overlaps_range (ptr
->data
, &r
))
326 gss_append_field_indicies (GnmSheetSlicer
const *gss
, GODataSlicerFieldType type
,
329 GArray
*tmp
= gss
->base
.fields
[type
];
330 unsigned int i
, n
= tmp
->len
;
331 for (i
= 0 ; i
< n
; i
++)
332 g_array_append_val (field_order
, g_array_index (tmp
, int, i
));
336 gnm_sheet_slicer_test_sort (GnmSheetSlicer
*gss
)
338 /* quick test to sort the cache based on the row/col */
339 GArray
*permutation
, *field_order
;
342 field_order
= g_array_sized_new (FALSE
, FALSE
, sizeof (unsigned int), gss
->base
.all_fields
->len
);
343 gss_append_field_indicies (gss
, GDS_FIELD_TYPE_ROW
, field_order
);
344 gss_append_field_indicies (gss
, GDS_FIELD_TYPE_COL
, field_order
);
346 n
= go_data_cache_num_items (gss
->base
.cache
);
347 permutation
= g_array_sized_new (FALSE
, FALSE
, sizeof (int), n
);
348 for (i
= 0 ; i
< n
; i
++)
349 g_array_append_val (permutation
, i
);
350 go_data_cache_permute (gss
->base
.cache
, field_order
, permutation
);
351 go_data_cache_dump (gss
->base
.cache
, field_order
, permutation
);
353 g_array_free (field_order
, TRUE
);
354 g_array_free (permutation
, TRUE
);
359 * gnm_sheet_slicer_regenerate:
360 * @gss: #GnmSheetSlicer
363 * See what we need to do then think about when portions belong in the GODataSlicer base.
367 gnm_sheet_slicer_regenerate (GnmSheetSlicer
*gss
)
370 GArray
*permutation
, *rows
;
373 g_return_if_fail (GNM_IS_SHEET_SLICER (gss
));
374 g_return_if_fail (IS_SHEET (gss
->sheet
));
375 g_return_if_fail (NULL
!= gss
->base
.cache
);
377 field_order
= g_array_sized_new (FALSE
, FALSE
, sizeof (unsigned int), gss
->base
.all_fields
->len
);
378 gss_append_field_indicies (gss
, GDS_FIELD_TYPE_ROW
, field_order
);
379 gss_append_field_indicies (gss
, GDS_FIELD_TYPE_COL
, field_order
);
381 n
= go_data_cache_num_items (gss
->base
.cache
);
386 gnm_sheet_slicer_get_layout (GnmSheetSlicer
const *gss
)
388 g_return_val_if_fail (GNM_IS_SHEET_SLICER (gss
), GSS_LAYOUT_XL_OUTLINE
);
393 gnm_sheet_slicer_set_layout (GnmSheetSlicer
*gss
, GnmSheetSlicerLayout l
)
395 g_return_if_fail (GNM_IS_SHEET_SLICER (gss
));
400 gnm_sheet_slicer_layout_get_type (void)
402 static GType etype
= 0;
404 static GEnumValue
const values
[] = {
405 { GSS_LAYOUT_XL_OUTLINE
, "GSS_LAYOUT_XL_OUTLINE", "xl-outline" },
406 { GSS_LAYOUT_XL_COMPACT
, "GSS_LAYOUT_XL_COMPACT", "xl-compact" },
407 { GSS_LAYOUT_XL_TABULAR
, "GSS_LAYOUT_XL_TABULAR", "xl-tabular" },
410 etype
= g_enum_register_static ("GnmSheetSlicerLayout", values
);