1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * preview-grid.c : Preview Grid Canvas Item
5 * Based upon "The Grid Gnome Canvas Item" a.k.a. Item-Grid
6 * (item-grid.c) Created by Miguel de Icaza (miguel@kernel.org)
8 * Author : Almer S. Tigelaar <almer@gnome.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <https://www.gnu.org/licenses/>.
24 #include <gnumeric-config.h>
26 #include "preview-grid-impl.h"
30 #include "cell-draw.h"
34 #include "rendered-value.h"
35 #include "sheet-style.h"
36 #include "style-border.h"
37 #include "style-color.h"
39 #include "gnm-marshalers.h"
41 #include <gsf/gsf-impl-utils.h>
43 static GocItemClass
*parent_klass
;
46 PREVIEW_GRID_PROP_RENDER_GRIDLINES
,
47 PREVIEW_GRID_PROP_DEFAULT_COL_WIDTH
,
48 PREVIEW_GRID_PROP_DEFAULT_ROW_HEIGHT
,
49 PREVIEW_GRID_PROP_DEFAULT_STYLE
,
50 PREVIEW_GRID_PROP_DEFAULT_VALUE
53 /*****************************************************************************/
56 pg_get_style (GnmPreviewGrid
*pg
, int col
, int row
)
58 GnmPreviewGridClass
*klass
= GNM_PREVIEW_GRID_GET_CLASS (pg
);
61 g_return_val_if_fail (col
>= 0 && col
< gnm_sheet_get_max_cols (pg
->sheet
), NULL
);
62 g_return_val_if_fail (row
>= 0 && row
< gnm_sheet_get_max_rows (pg
->sheet
), NULL
);
63 g_return_val_if_fail (klass
!= NULL
, NULL
);
65 if (klass
->get_cell_style
) {
66 style
= klass
->get_cell_style (pg
, col
, row
);
71 return pg
->defaults
.style
;
75 pg_fetch_cell (GnmPreviewGrid
*pg
, int col
, int row
)
77 GnmPreviewGridClass
*klass
= GNM_PREVIEW_GRID_GET_CLASS (pg
);
81 g_return_val_if_fail (klass
!= NULL
, NULL
);
82 g_return_val_if_fail (pg
!= NULL
, NULL
);
83 g_return_val_if_fail (col
>= 0 && col
< gnm_sheet_get_max_cols (pg
->sheet
), NULL
);
84 g_return_val_if_fail (row
>= 0 && row
< gnm_sheet_get_max_rows (pg
->sheet
), NULL
);
86 if (NULL
!= klass
->get_cell_value
)
87 v
= (klass
->get_cell_value
) (pg
, col
, row
);
89 v
= value_dup (pg
->defaults
.value
);
91 cell
= sheet_cell_fetch (pg
->sheet
, col
, row
);
92 gnm_cell_set_value (cell
, v
);
94 gnm_cell_render_value (cell
, TRUE
);
103 * @row_origin: if not null the origin of the row containing pixel @y is put here
105 * Return value: Row containing pixel y (and origin in @row_origin)
108 pg_get_row_offset (GnmPreviewGrid
*pg
, int const y
, int *row_origin
)
112 int const h
= pg
->defaults
.row_height
;
114 g_return_val_if_fail (pg
!= NULL
, 0);
117 if (y
<= (pixel
+ h
) || h
== 0) {
123 } while (++row
< gnm_sheet_get_max_rows (pg
->sheet
));
128 return gnm_sheet_get_last_row (pg
->sheet
);
134 * @col_origin: if not null the origin of the column containing pixel @x is put here
136 * Return value: Column containing pixel x (and origin in @col_origin)
139 pg_get_col_offset (GnmPreviewGrid
*pg
, int const x
, int *col_origin
)
145 g_return_val_if_fail (pg
!= NULL
, 0);
148 w
= pg
->defaults
.col_width
;
149 if (x
<= (pixel
+ w
) || w
== 0) {
155 } while (++col
< gnm_sheet_get_max_cols (pg
->sheet
));
160 return gnm_sheet_get_last_col (pg
->sheet
);
164 preview_grid_update_bounds (GocItem
*item
)
168 item
->x1
= INT_MAX
/2; /* FIXME add some num cols/rows abilities */
169 item
->y1
= INT_MAX
/2; /* FIXME and some flags to decide how to adapt */
173 preview_grid_draw_background (cairo_t
*cr
, GnmPreviewGrid
const *pg
, GnmStyle
const *mstyle
,
174 int col
, int row
, int x
, int y
, int w
, int h
)
176 if (gnm_pattern_background_set (mstyle
, cr
, FALSE
, NULL
)) {
177 cairo_rectangle (cr
, x
, y
, w
+1, h
+1);
180 gnm_style_border_draw_diag (mstyle
, cr
, x
, y
, x
+w
, y
+h
);
184 pg_style_get_row (GnmPreviewGrid
*pg
, GnmStyleRow
*sr
)
186 int const row
= sr
->row
;
189 for (col
= sr
->start_col
; col
<= sr
->end_col
; col
++) {
190 GnmStyle
const *style
= pg_get_style (pg
, col
, row
);
191 sheet_style_set_pos (pg
->sheet
, col
, row
,
192 gnm_style_dup (style
));
195 sheet_style_get_row (pg
->sheet
, sr
);
198 /* no spans or merges */
200 preview_grid_draw_region (GocItem
const *item
, cairo_t
*cr
,
201 double x0
, double y0
, double x1
, double y1
)
203 GnmPreviewGrid
*pg
= GNM_PREVIEW_GRID (item
);
205 /* To ensure that far and near borders get drawn we pretend to draw +-2
206 * pixels around the target area which would include the surrounding
207 * borders if necessary */
208 /* TODO : there is an opportunity to speed up the redraw loop by only
209 * painting the borders of the edges and not the content.
210 * However, that feels like more hassle that it is worth. Look into this someday.
212 int x
, y
, col
, row
, n
;
213 int const start_col
= pg_get_col_offset (pg
, x0
- 2, &x
);
214 int end_col
= pg_get_col_offset (pg
, x1
+ 2, NULL
);
216 int start_row
= pg_get_row_offset (pg
, y0
- 2, &y
);
217 int end_row
= pg_get_row_offset (pg
, y1
+ 2, NULL
);
219 int row_height
= pg
->defaults
.row_height
;
221 GnmStyleRow sr
, next_sr
;
222 GnmStyle
const **styles
;
223 GnmBorder
const **borders
, **prev_vert
;
224 GnmBorder
const *none
= pg
->gridlines
? gnm_style_border_none () : NULL
;
225 gpointer
*sr_array_data
;
227 int *colwidths
= NULL
;
229 gnm_style_border_none_set_color (style_color_grid ());
232 * allocate a single blob of memory for all 8 arrays of pointers.
233 * - 6 arrays of n GnmBorder const *
234 * - 2 arrays of n GnmStyle const *
236 n
= end_col
- start_col
+ 3; /* 1 before, 1 after, 1 fencepost */
237 sr_array_data
= g_new (gpointer
, n
* 8);
238 style_row_init (&prev_vert
, &sr
, &next_sr
, start_col
, end_col
,
239 sr_array_data
, !pg
->gridlines
);
241 /* load up the styles for the first row */
242 next_sr
.row
= sr
.row
= row
= start_row
;
243 pg_style_get_row (pg
, &sr
);
245 /* Collect the column widths */
246 colwidths
= g_new (int, n
);
247 colwidths
-= start_col
;
248 for (col
= start_col
; col
<= end_col
; col
++)
249 colwidths
[col
] = pg
->defaults
.col_width
;
251 /* Fill entire region with default background (even past far edge) */
252 gtk_render_background (goc_item_get_style_context (item
),
253 cr
, diff_x
, diff_y
, x1
- x0
, y1
- y0
);
255 for (y
= diff_y
; row
<= end_row
; row
= sr
.row
= next_sr
.row
) {
256 if (++next_sr
.row
> end_row
) {
257 for (col
= start_col
; col
<= end_col
; ++col
)
258 next_sr
.vertical
[col
] =
259 next_sr
.bottom
[col
] = none
;
261 pg_style_get_row (pg
, &next_sr
);
263 for (col
= start_col
, x
= diff_x
; col
<= end_col
; col
++) {
264 GnmStyle
const *style
= sr
.styles
[col
];
265 GnmCell
const *cell
= pg_fetch_cell (pg
, col
, row
);
267 preview_grid_draw_background (cr
, pg
,
268 style
, col
, row
, x
, y
,
269 colwidths
[col
], row_height
);
271 if (!gnm_cell_is_empty (cell
))
273 x
, y
, colwidths
[col
], row_height
,
279 gnm_style_borders_row_draw
281 diff_x
, y
, y
+ row_height
,
282 colwidths
, TRUE
, 1 /* cheat dir == 1 for now */);
284 /* roll the pointers */
285 borders
= prev_vert
; prev_vert
= sr
.vertical
;
286 sr
.vertical
= next_sr
.vertical
; next_sr
.vertical
= borders
;
287 borders
= sr
.top
; sr
.top
= sr
.bottom
;
288 sr
.bottom
= next_sr
.top
= next_sr
.bottom
; next_sr
.bottom
= borders
;
289 styles
= sr
.styles
; sr
.styles
= next_sr
.styles
; next_sr
.styles
= styles
;
294 g_free (sr_array_data
);
295 g_free (colwidths
+ start_col
); // Offset reverts -= from above
300 preview_grid_distance (GocItem
*item
, double cx
, double cy
,
301 GocItem
**actual_item
)
308 preview_grid_set_property (GObject
*obj
, guint param_id
,
309 GValue
const *value
, GParamSpec
*pspec
)
311 GnmPreviewGrid
*pg
= GNM_PREVIEW_GRID (obj
);
314 case PREVIEW_GRID_PROP_RENDER_GRIDLINES
:
315 pg
->gridlines
= g_value_get_boolean (value
);
317 case PREVIEW_GRID_PROP_DEFAULT_COL_WIDTH
:
318 pg
->defaults
.col_width
= g_value_get_uint (value
);
320 case PREVIEW_GRID_PROP_DEFAULT_ROW_HEIGHT
:
321 pg
->defaults
.row_height
= g_value_get_uint (value
);
323 case PREVIEW_GRID_PROP_DEFAULT_STYLE
: {
324 GnmStyle
*style
= g_value_dup_boxed (value
);
325 g_return_if_fail (style
!= NULL
);
326 gnm_style_unref (pg
->defaults
.style
);
327 pg
->defaults
.style
= style
;
330 case PREVIEW_GRID_PROP_DEFAULT_VALUE
: {
331 GnmValue
*val
= g_value_dup_boxed (value
);
332 g_return_if_fail (val
!= NULL
);
333 value_release (pg
->defaults
.value
);
334 pg
->defaults
.value
= val
;
337 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
338 return; /* NOTE : RETURN */
341 goc_item_invalidate (GOC_ITEM (obj
));
345 preview_grid_dispose (GObject
*obj
)
347 GnmPreviewGrid
*pg
= GNM_PREVIEW_GRID (obj
);
349 if (pg
->defaults
.style
!= NULL
) {
350 gnm_style_unref (pg
->defaults
.style
);
351 pg
->defaults
.style
= NULL
;
353 value_release (pg
->defaults
.value
);
354 pg
->defaults
.value
= NULL
;
356 g_clear_object (&pg
->sheet
);
358 G_OBJECT_CLASS (parent_klass
)->dispose (obj
);
362 gnm_preview_grid_init (GnmPreviewGrid
*pg
)
364 pg
->sheet
= g_object_new (GNM_SHEET_TYPE
,
368 pg
->gridlines
= FALSE
;
369 pg
->defaults
.col_width
= 64;
370 pg
->defaults
.row_height
= 17;
371 pg
->defaults
.style
= gnm_style_new_default ();
372 pg
->defaults
.value
= value_new_empty ();
376 gnm_preview_grid_class_init (GObjectClass
*gobject_klass
)
378 GocItemClass
*item_klass
= (GocItemClass
*)gobject_klass
;
380 parent_klass
= g_type_class_peek_parent (gobject_klass
);
382 gobject_klass
->set_property
= preview_grid_set_property
;
383 gobject_klass
->dispose
= preview_grid_dispose
;
384 g_object_class_install_property
385 (gobject_klass
, PREVIEW_GRID_PROP_RENDER_GRIDLINES
,
386 g_param_spec_boolean ("render-gridlines", NULL
, NULL
,
388 GSF_PARAM_STATIC
| G_PARAM_WRITABLE
));
389 g_object_class_install_property
390 (gobject_klass
, PREVIEW_GRID_PROP_DEFAULT_COL_WIDTH
,
391 g_param_spec_uint ("default-col-width", NULL
, NULL
,
393 GSF_PARAM_STATIC
| G_PARAM_WRITABLE
));
394 g_object_class_install_property
395 (gobject_klass
, PREVIEW_GRID_PROP_DEFAULT_ROW_HEIGHT
,
396 g_param_spec_uint ("default-row-height", NULL
, NULL
,
398 GSF_PARAM_STATIC
| G_PARAM_WRITABLE
));
399 g_object_class_install_property
400 (gobject_klass
, PREVIEW_GRID_PROP_DEFAULT_STYLE
,
401 g_param_spec_boxed ("default-style", NULL
, NULL
,
402 gnm_style_get_type (),
403 GSF_PARAM_STATIC
| G_PARAM_WRITABLE
));
404 g_object_class_install_property
405 (gobject_klass
, PREVIEW_GRID_PROP_DEFAULT_VALUE
,
406 g_param_spec_boxed ("default-value", NULL
, NULL
,
407 gnm_value_get_type (),
408 GSF_PARAM_STATIC
| G_PARAM_WRITABLE
));
410 item_klass
->update_bounds
= preview_grid_update_bounds
;
411 item_klass
->draw_region
= preview_grid_draw_region
;
412 item_klass
->distance
= preview_grid_distance
;
415 GSF_CLASS (GnmPreviewGrid
, gnm_preview_grid
,
416 gnm_preview_grid_class_init
, gnm_preview_grid_init
,