2 * preview-grid.c : Preview Grid Canvas Item
4 * Based upon "The Grid Gnome Canvas Item" a.k.a. Item-Grid
5 * (item-grid.c) Created by Miguel de Icaza (miguel@kernel.org)
7 * Author : Almer S. Tigelaar <almer@gnome.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <https://www.gnu.org/licenses/>.
23 #include <gnumeric-config.h>
25 #include <preview-grid-impl.h>
29 #include <cell-draw.h>
33 #include <rendered-value.h>
34 #include <sheet-style.h>
35 #include <style-border.h>
36 #include <style-color.h>
38 #include <gnm-marshalers.h>
40 #include <gsf/gsf-impl-utils.h>
42 static GocItemClass
*parent_klass
;
45 PREVIEW_GRID_PROP_RENDER_GRIDLINES
,
46 PREVIEW_GRID_PROP_DEFAULT_COL_WIDTH
,
47 PREVIEW_GRID_PROP_DEFAULT_ROW_HEIGHT
,
48 PREVIEW_GRID_PROP_DEFAULT_STYLE
,
49 PREVIEW_GRID_PROP_DEFAULT_VALUE
52 /*****************************************************************************/
55 pg_get_style (GnmPreviewGrid
*pg
, int col
, int row
)
57 GnmPreviewGridClass
*klass
= GNM_PREVIEW_GRID_GET_CLASS (pg
);
60 g_return_val_if_fail (col
>= 0 && col
< gnm_sheet_get_max_cols (pg
->sheet
), NULL
);
61 g_return_val_if_fail (row
>= 0 && row
< gnm_sheet_get_max_rows (pg
->sheet
), NULL
);
62 g_return_val_if_fail (klass
!= NULL
, NULL
);
64 if (klass
->get_cell_style
) {
65 style
= klass
->get_cell_style (pg
, col
, row
);
70 return pg
->defaults
.style
;
74 pg_fetch_cell (GnmPreviewGrid
*pg
, int col
, int row
)
76 GnmPreviewGridClass
*klass
= GNM_PREVIEW_GRID_GET_CLASS (pg
);
80 g_return_val_if_fail (klass
!= NULL
, NULL
);
81 g_return_val_if_fail (pg
!= NULL
, NULL
);
82 g_return_val_if_fail (col
>= 0 && col
< gnm_sheet_get_max_cols (pg
->sheet
), NULL
);
83 g_return_val_if_fail (row
>= 0 && row
< gnm_sheet_get_max_rows (pg
->sheet
), NULL
);
85 if (NULL
!= klass
->get_cell_value
)
86 v
= (klass
->get_cell_value
) (pg
, col
, row
);
88 v
= value_dup (pg
->defaults
.value
);
90 cell
= sheet_cell_fetch (pg
->sheet
, col
, row
);
91 gnm_cell_set_value (cell
, v
);
93 gnm_cell_render_value (cell
, TRUE
);
102 * @row_origin: if not null the origin of the row containing pixel @y is put here
104 * Return value: Row containing pixel y (and origin in @row_origin)
107 pg_get_row_offset (GnmPreviewGrid
*pg
, int const y
, int *row_origin
)
111 int const h
= pg
->defaults
.row_height
;
113 g_return_val_if_fail (pg
!= NULL
, 0);
116 if (y
<= (pixel
+ h
) || h
== 0) {
122 } while (++row
< gnm_sheet_get_max_rows (pg
->sheet
));
127 return gnm_sheet_get_last_row (pg
->sheet
);
133 * @col_origin: if not null the origin of the column containing pixel @x is put here
135 * Return value: Column containing pixel x (and origin in @col_origin)
138 pg_get_col_offset (GnmPreviewGrid
*pg
, int const x
, int *col_origin
)
144 g_return_val_if_fail (pg
!= NULL
, 0);
147 w
= pg
->defaults
.col_width
;
148 if (x
<= (pixel
+ w
) || w
== 0) {
154 } while (++col
< gnm_sheet_get_max_cols (pg
->sheet
));
159 return gnm_sheet_get_last_col (pg
->sheet
);
163 preview_grid_update_bounds (GocItem
*item
)
167 item
->x1
= INT_MAX
/2; /* FIXME add some num cols/rows abilities */
168 item
->y1
= INT_MAX
/2; /* FIXME and some flags to decide how to adapt */
172 preview_grid_draw_background (cairo_t
*cr
, GnmPreviewGrid
const *pg
, GnmStyle
const *mstyle
,
173 int col
, int row
, int x
, int y
, int w
, int h
)
175 if (gnm_pattern_background_set (mstyle
, cr
, FALSE
, NULL
)) {
176 cairo_rectangle (cr
, x
, y
, w
+1, h
+1);
179 gnm_style_border_draw_diag (mstyle
, cr
, x
, y
, x
+w
, y
+h
);
183 pg_style_get_row (GnmPreviewGrid
*pg
, GnmStyleRow
*sr
)
185 int const row
= sr
->row
;
188 for (col
= sr
->start_col
; col
<= sr
->end_col
; col
++) {
189 GnmStyle
const *style
= pg_get_style (pg
, col
, row
);
190 sheet_style_set_pos (pg
->sheet
, col
, row
,
191 gnm_style_dup (style
));
194 sheet_style_get_row (pg
->sheet
, sr
);
197 /* no spans or merges */
199 preview_grid_draw_region (GocItem
const *item
, cairo_t
*cr
,
200 double x0
, double y0
, double x1
, double y1
)
202 GnmPreviewGrid
*pg
= GNM_PREVIEW_GRID (item
);
204 /* To ensure that far and near borders get drawn we pretend to draw +-2
205 * pixels around the target area which would include the surrounding
206 * borders if necessary */
207 /* TODO : there is an opportunity to speed up the redraw loop by only
208 * painting the borders of the edges and not the content.
209 * However, that feels like more hassle that it is worth. Look into this someday.
211 int x
, y
, col
, row
, n
;
212 int const start_col
= pg_get_col_offset (pg
, x0
- 2, &x
);
213 int end_col
= pg_get_col_offset (pg
, x1
+ 2, NULL
);
215 int start_row
= pg_get_row_offset (pg
, y0
- 2, &y
);
216 int end_row
= pg_get_row_offset (pg
, y1
+ 2, NULL
);
218 int row_height
= pg
->defaults
.row_height
;
220 GnmStyleRow sr
, next_sr
;
221 GnmStyle
const **styles
;
222 GnmBorder
const **borders
, **prev_vert
;
223 GnmBorder
const *none
= pg
->gridlines
? gnm_style_border_none () : NULL
;
224 gpointer
*sr_array_data
;
226 int *colwidths
= NULL
;
228 gnm_style_border_none_set_color (style_color_grid ());
231 * allocate a single blob of memory for all 8 arrays of pointers.
232 * - 6 arrays of n GnmBorder const *
233 * - 2 arrays of n GnmStyle const *
235 n
= end_col
- start_col
+ 3; /* 1 before, 1 after, 1 fencepost */
236 sr_array_data
= g_new (gpointer
, n
* 8);
237 style_row_init (&prev_vert
, &sr
, &next_sr
, start_col
, end_col
,
238 sr_array_data
, !pg
->gridlines
);
240 /* load up the styles for the first row */
241 next_sr
.row
= sr
.row
= row
= start_row
;
242 pg_style_get_row (pg
, &sr
);
244 /* Collect the column widths */
245 colwidths
= g_new (int, n
);
246 colwidths
-= start_col
;
247 for (col
= start_col
; col
<= end_col
; col
++)
248 colwidths
[col
] = pg
->defaults
.col_width
;
250 /* Fill entire region with default background (even past far edge) */
251 gtk_render_background (goc_item_get_style_context (item
),
252 cr
, diff_x
, diff_y
, x1
- x0
, y1
- y0
);
254 for (y
= diff_y
; row
<= end_row
; row
= sr
.row
= next_sr
.row
) {
255 if (++next_sr
.row
> end_row
) {
256 for (col
= start_col
; col
<= end_col
; ++col
)
257 next_sr
.vertical
[col
] =
258 next_sr
.bottom
[col
] = none
;
260 pg_style_get_row (pg
, &next_sr
);
262 for (col
= start_col
, x
= diff_x
; col
<= end_col
; col
++) {
263 GnmStyle
const *style
= sr
.styles
[col
];
264 GnmCell
const *cell
= pg_fetch_cell (pg
, col
, row
);
266 preview_grid_draw_background (cr
, pg
,
267 style
, col
, row
, x
, y
,
268 colwidths
[col
], row_height
);
270 if (!gnm_cell_is_empty (cell
))
272 x
, y
, colwidths
[col
], row_height
,
278 gnm_style_borders_row_draw
280 diff_x
, y
, y
+ row_height
,
281 colwidths
, TRUE
, 1 /* cheat dir == 1 for now */);
283 /* roll the pointers */
284 borders
= prev_vert
; prev_vert
= sr
.vertical
;
285 sr
.vertical
= next_sr
.vertical
; next_sr
.vertical
= borders
;
286 borders
= sr
.top
; sr
.top
= sr
.bottom
;
287 sr
.bottom
= next_sr
.top
= next_sr
.bottom
; next_sr
.bottom
= borders
;
288 styles
= sr
.styles
; sr
.styles
= next_sr
.styles
; next_sr
.styles
= styles
;
293 g_free (sr_array_data
);
294 g_free (colwidths
+ start_col
); // Offset reverts -= from above
299 preview_grid_distance (GocItem
*item
, double cx
, double cy
,
300 GocItem
**actual_item
)
307 preview_grid_set_property (GObject
*obj
, guint param_id
,
308 GValue
const *value
, GParamSpec
*pspec
)
310 GnmPreviewGrid
*pg
= GNM_PREVIEW_GRID (obj
);
313 case PREVIEW_GRID_PROP_RENDER_GRIDLINES
:
314 pg
->gridlines
= g_value_get_boolean (value
);
316 case PREVIEW_GRID_PROP_DEFAULT_COL_WIDTH
:
317 pg
->defaults
.col_width
= g_value_get_uint (value
);
319 case PREVIEW_GRID_PROP_DEFAULT_ROW_HEIGHT
:
320 pg
->defaults
.row_height
= g_value_get_uint (value
);
322 case PREVIEW_GRID_PROP_DEFAULT_STYLE
: {
323 GnmStyle
*style
= g_value_dup_boxed (value
);
324 g_return_if_fail (style
!= NULL
);
325 gnm_style_unref (pg
->defaults
.style
);
326 pg
->defaults
.style
= style
;
329 case PREVIEW_GRID_PROP_DEFAULT_VALUE
: {
330 GnmValue
*val
= g_value_dup_boxed (value
);
331 g_return_if_fail (val
!= NULL
);
332 value_release (pg
->defaults
.value
);
333 pg
->defaults
.value
= val
;
336 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
337 return; /* NOTE : RETURN */
340 goc_item_invalidate (GOC_ITEM (obj
));
344 preview_grid_dispose (GObject
*obj
)
346 GnmPreviewGrid
*pg
= GNM_PREVIEW_GRID (obj
);
348 if (pg
->defaults
.style
!= NULL
) {
349 gnm_style_unref (pg
->defaults
.style
);
350 pg
->defaults
.style
= NULL
;
352 value_release (pg
->defaults
.value
);
353 pg
->defaults
.value
= NULL
;
355 g_clear_object (&pg
->sheet
);
357 G_OBJECT_CLASS (parent_klass
)->dispose (obj
);
361 gnm_preview_grid_init (GnmPreviewGrid
*pg
)
363 pg
->sheet
= g_object_new (GNM_SHEET_TYPE
,
367 pg
->gridlines
= FALSE
;
368 pg
->defaults
.col_width
= 64;
369 pg
->defaults
.row_height
= 17;
370 pg
->defaults
.style
= gnm_style_new_default ();
371 pg
->defaults
.value
= value_new_empty ();
375 gnm_preview_grid_class_init (GObjectClass
*gobject_klass
)
377 GocItemClass
*item_klass
= (GocItemClass
*)gobject_klass
;
379 parent_klass
= g_type_class_peek_parent (gobject_klass
);
381 gobject_klass
->set_property
= preview_grid_set_property
;
382 gobject_klass
->dispose
= preview_grid_dispose
;
383 g_object_class_install_property
384 (gobject_klass
, PREVIEW_GRID_PROP_RENDER_GRIDLINES
,
385 g_param_spec_boolean ("render-gridlines", NULL
, NULL
,
387 GSF_PARAM_STATIC
| G_PARAM_WRITABLE
));
388 g_object_class_install_property
389 (gobject_klass
, PREVIEW_GRID_PROP_DEFAULT_COL_WIDTH
,
390 g_param_spec_uint ("default-col-width", NULL
, NULL
,
392 GSF_PARAM_STATIC
| G_PARAM_WRITABLE
));
393 g_object_class_install_property
394 (gobject_klass
, PREVIEW_GRID_PROP_DEFAULT_ROW_HEIGHT
,
395 g_param_spec_uint ("default-row-height", NULL
, NULL
,
397 GSF_PARAM_STATIC
| G_PARAM_WRITABLE
));
398 g_object_class_install_property
399 (gobject_klass
, PREVIEW_GRID_PROP_DEFAULT_STYLE
,
400 g_param_spec_boxed ("default-style", NULL
, NULL
,
401 gnm_style_get_type (),
402 GSF_PARAM_STATIC
| G_PARAM_WRITABLE
));
403 g_object_class_install_property
404 (gobject_klass
, PREVIEW_GRID_PROP_DEFAULT_VALUE
,
405 g_param_spec_boxed ("default-value", NULL
, NULL
,
406 gnm_value_get_type (),
407 GSF_PARAM_STATIC
| G_PARAM_WRITABLE
));
409 item_klass
->update_bounds
= preview_grid_update_bounds
;
410 item_klass
->draw_region
= preview_grid_draw_region
;
411 item_klass
->distance
= preview_grid_distance
;
414 GSF_CLASS (GnmPreviewGrid
, gnm_preview_grid
,
415 gnm_preview_grid_class_init
, gnm_preview_grid_init
,