2 * clipboard.c: A temporary store for contents from a worksheet
4 * Copyright (C) 2000-2008 Jody Goldberg (jody@gnome.org)
5 * 1999 Miguel de Icaza (miguel@gnu.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
22 #include <gnumeric-config.h>
24 #include <clipboard.h>
28 #include <sheet-style.h>
29 #include <sheet-merge.h>
30 #include <dependent.h>
31 #include <selection.h>
32 #include <command-context.h>
33 #include <workbook-control.h>
40 #include <stf-parse.h>
41 #include <gnm-format.h>
42 #include <sheet-object-cell-comment.h>
44 #include <glib/gi18n-lib.h>
47 #include <goffice/goffice.h>
49 #ifndef USE_CELL_COPY_POOLS
50 #define USE_CELL_COPY_POOLS 1
53 #if USE_CELL_COPY_POOLS
54 /* Memory pool for GnmCellCopy. */
55 static GOMemChunk
*cell_copy_pool
;
56 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
57 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
59 #define CHUNK_ALLOC(T,c) g_new (T,1)
60 #define CHUNK_FREE(p,v) g_free ((v))
63 /* creating a boxed type for GnmCellCopy (needed by introspection) */
65 pointer_dup (gpointer
*cc
)
71 gnm_cell_copy_get_type (void)
76 t
= g_boxed_type_register_static ("GnmCellCopy",
77 (GBoxedCopyFunc
)pointer_dup
,
78 (GBoxedFreeFunc
)pointer_dup
);
83 /* creating a boxed type for GnmPasteTarget (needed by introspection) */
85 static GnmPasteTarget
*
86 gnm_paste_target_copy (GnmPasteTarget
*pt
)
88 return g_memdup (pt
, sizeof (*pt
));
92 gnm_paste_target_get_type (void)
97 t
= g_boxed_type_register_static ("GnmPasteTarget",
98 (GBoxedCopyFunc
)gnm_paste_target_copy
,
99 (GBoxedFreeFunc
)g_free
);
105 gnm_paste_target_new (Sheet
*sheet
, GnmRange
*r
, GnmPasteFlags flags
)
107 GnmPasteTarget
*res
= g_new (GnmPasteTarget
, 1);
108 paste_target_init (res
, sheet
, r
, flags
);
114 cell_has_expr_or_number_or_blank (GnmCell
const * cell
)
116 return (gnm_cell_is_empty (cell
) ||
117 (cell
!= NULL
&& gnm_cell_is_number (cell
)) ||
118 (cell
!= NULL
&& gnm_cell_has_expr (cell
)));
121 static GnmExpr
const *
122 contents_as_expr (GnmExprTop
const *texpr
, GnmValue
const *val
)
125 return gnm_expr_copy (texpr
->expr
);
126 if (VALUE_IS_EMPTY (val
))
127 return gnm_expr_new_constant (value_new_float (0.0));
128 if (VALUE_IS_NUMBER (val
))
129 return gnm_expr_new_constant (value_dup (val
));
134 paste_op_to_expr_op (int paste_flags
)
136 g_return_val_if_fail (paste_flags
& PASTE_OPER_MASK
, 0);
138 if (paste_flags
& PASTE_OPER_ADD
)
139 return GNM_EXPR_OP_ADD
;
140 else if (paste_flags
& PASTE_OPER_SUB
)
141 return GNM_EXPR_OP_SUB
;
142 else if (paste_flags
& PASTE_OPER_MULT
)
143 return GNM_EXPR_OP_MULT
;
144 else if (paste_flags
& PASTE_OPER_DIV
)
145 return GNM_EXPR_OP_DIV
;
151 paste_cell_with_operation (Sheet
*dst_sheet
,
152 int target_col
, int target_row
,
153 GnmExprRelocateInfo
const *rinfo
,
154 GnmCellCopy
const *src
,
160 if (src
->texpr
== NULL
&&
161 !VALUE_IS_EMPTY (src
->val
) &&
162 !VALUE_IS_NUMBER (src
->val
))
165 dst
= sheet_cell_fetch (dst_sheet
, target_col
, target_row
);
166 if (!cell_has_expr_or_number_or_blank (dst
))
169 op
= paste_op_to_expr_op (paste_flags
);
170 /* FIXME : This does not handle arrays, linked cells, ranges, etc. */
171 if ((paste_flags
& PASTE_CONTENTS
) &&
172 (NULL
!= src
->texpr
|| gnm_cell_has_expr (dst
))) {
173 GnmExpr
const *old_expr
= contents_as_expr (dst
->base
.texpr
, dst
->value
);
174 GnmExpr
const *copied_expr
= contents_as_expr (src
->texpr
, src
->val
);
175 GnmExprTop
const *res
= gnm_expr_top_new (gnm_expr_new_binary (old_expr
, op
, copied_expr
));
176 GnmExprTop
const *relo
= gnm_expr_top_relocate (res
, rinfo
, FALSE
);
178 gnm_cell_set_expr (dst
, relo
);
179 gnm_expr_top_unref (relo
);
181 gnm_cell_set_expr (dst
, res
);
182 gnm_expr_top_unref (res
);
186 GnmExpr
const *expr
= gnm_expr_new_binary (
187 gnm_expr_new_constant (value_dup (dst
->value
)),
189 gnm_expr_new_constant (value_dup (src
->val
)));
190 GnmExprTop
const *texpr
= gnm_expr_top_new (expr
);
192 eval_pos_init_cell (&pos
, dst
);
193 pos
.dep
= NULL
; /* no dynamic deps */
194 value
= gnm_expr_top_eval (texpr
, &pos
,
195 GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
196 gnm_expr_top_unref (texpr
);
197 gnm_cell_set_value (dst
, value
);
201 /* NOTE : Make sure to set up any merged regions in the target range BEFORE
205 paste_link (GnmPasteTarget
const *pt
, int top
, int left
,
206 GnmCellRegion
const *cr
)
209 GnmCellRef source_cell_ref
;
212 /* Not possible to link to arbitrary (non gnumeric) sources yet. */
213 /* TODO : eventually support interprocess gnumeric links */
214 if (cr
->origin_sheet
== NULL
)
217 /* TODO : support relative links ? */
218 source_cell_ref
.col_relative
= 0;
219 source_cell_ref
.row_relative
= 0;
220 source_cell_ref
.sheet
= (cr
->origin_sheet
!= pt
->sheet
)
221 ? cr
->origin_sheet
: NULL
;
223 for (x
= 0 ; x
< cr
->cols
; x
++, pos
.col
++) {
224 source_cell_ref
.col
= cr
->base
.col
+ x
;
226 for (y
= 0 ; y
< cr
->rows
; y
++, pos
.row
++) {
227 GnmExprTop
const *texpr
;
229 sheet_cell_fetch (pt
->sheet
, pos
.col
, pos
.row
);
231 /* This could easily be made smarter */
232 if (!gnm_cell_is_merged (cell
) &&
233 gnm_sheet_merge_contains_pos (pt
->sheet
, &pos
))
235 source_cell_ref
.row
= cr
->base
.row
+ y
;
236 texpr
= gnm_expr_top_new (gnm_expr_new_cellref (&source_cell_ref
));
237 gnm_cell_set_expr (cell
, texpr
);
238 gnm_expr_top_unref (texpr
);
243 struct paste_cell_data
{
244 GnmPasteTarget
const *pt
;
245 GnmCellRegion
const *cr
;
247 GnmExprRelocateInfo rinfo
;
248 gboolean translate_dates
;
253 * @target_col: Column to put the cell into
254 * @target_row: Row to put the cell into.
255 * @src: A #GnmCellCopy with the content to paste
256 * @paste_flags: Bit mask that describes the paste options.
258 * Pastes a cell in the spreadsheet.
261 paste_cell (int target_col
, int target_row
,
262 GnmCellCopy
const *src
,
263 const struct paste_cell_data
*dat
)
265 Sheet
*dst_sheet
= dat
->pt
->sheet
;
266 int paste_flags
= dat
->pt
->paste_flags
;
268 if (paste_flags
& PASTE_OPER_MASK
)
269 paste_cell_with_operation (dst_sheet
, target_col
, target_row
,
270 &dat
->rinfo
, src
, paste_flags
);
272 GnmCell
*dst
= sheet_cell_fetch (dst_sheet
, target_col
, target_row
);
273 if (NULL
!= src
->texpr
&& (paste_flags
& PASTE_CONTENTS
)) {
274 GnmExprTop
const *relo
= gnm_expr_top_relocate (
275 src
->texpr
, &dat
->rinfo
, FALSE
);
276 if (paste_flags
& PASTE_TRANSPOSE
) {
277 GnmExprTop
const *trelo
=
278 gnm_expr_top_transpose (relo
? relo
: src
->texpr
);
281 gnm_expr_top_unref (relo
);
284 } else if (!relo
&& gnm_expr_top_is_array_corner (src
->texpr
)) {
285 /* We must not share array expressions. */
286 relo
= gnm_expr_top_new (gnm_expr_copy (src
->texpr
->expr
));
288 gnm_cell_set_expr_and_value (dst
, relo
? relo
: src
->texpr
,
289 value_dup (src
->val
), TRUE
);
291 gnm_expr_top_unref (relo
);
292 } else if (src
->val
) {
293 GnmValue
*newval
= NULL
;
294 GnmValue
const *oldval
= src
->val
;
296 if (dat
->translate_dates
&& oldval
&& VALUE_IS_FLOAT (oldval
)) {
297 GOFormat
const *fmt
= VALUE_FMT (oldval
)
299 : gnm_style_get_format (gnm_cell_get_style (dst
));
300 if (go_format_is_date (fmt
) > 0) {
301 gnm_float fnew
= go_date_conv_translate
302 (value_get_as_float (oldval
),
304 sheet_date_conv (dst_sheet
));
305 newval
= value_new_float (fnew
);
306 value_set_fmt (newval
, VALUE_FMT (oldval
));
311 newval
= value_dup (src
->val
);
312 gnm_cell_set_value (dst
, newval
);
318 paste_object (GnmPasteTarget
const *pt
, SheetObject
const *src
, int left
, int top
)
321 SheetObjectAnchor tmp
;
323 tmp
= *sheet_object_get_anchor (src
);
324 if (G_OBJECT_TYPE (src
) == GNM_CELL_COMMENT_TYPE
) {
325 if ((pt
->paste_flags
& PASTE_COMMENTS
) &&
326 (pt
->paste_flags
& PASTE_IGNORE_COMMENTS_AT_ORIGIN
&&
327 tmp
.cell_bound
.start
.col
== 0 &&
328 tmp
.cell_bound
.start
.row
== 0))
330 } else if (!(pt
->paste_flags
& PASTE_OBJECTS
))
333 if (NULL
== (dst
= sheet_object_dup (src
)))
336 if (pt
->paste_flags
& PASTE_TRANSPOSE
) {
340 range_transpose (&tmp
.cell_bound
, pt
->sheet
, &origin
);
342 range_translate (&tmp
.cell_bound
, pt
->sheet
, left
, top
);
343 sheet_object_set_anchor (dst
, &tmp
);
344 sheet_object_set_sheet (dst
, pt
->sheet
);
345 g_object_unref (dst
);
349 cb_paste_cell (GnmCellCopy
const *src
, gconstpointer ignore
,
350 struct paste_cell_data
*dat
)
352 int target_col
= dat
->top_left
.col
;
353 int target_row
= dat
->top_left
.row
;
355 if (dat
->pt
->paste_flags
& PASTE_TRANSPOSE
) {
356 target_col
+= src
->offset
.row
;
357 target_row
+= src
->offset
.col
;
358 } else if (dat
->pt
->paste_flags
& PASTE_FLIP_H
) {
359 target_col
+= dat
->cr
->cols
- src
->offset
.col
- 1;
360 target_row
+= src
->offset
.row
;
361 } else if (dat
->pt
->paste_flags
& PASTE_FLIP_V
) {
362 target_col
+= src
->offset
.col
;
363 target_row
+= dat
->cr
->rows
- src
->offset
.row
- 1;
365 target_col
+= src
->offset
.col
;
366 target_row
+= src
->offset
.row
;
369 dat
->rinfo
.pos
.sheet
= dat
->pt
->sheet
;
370 if (dat
->pt
->paste_flags
& PASTE_EXPR_LOCAL_RELOCATE
) {
371 dat
->rinfo
.pos
.eval
.col
= dat
->cr
->base
.col
+ src
->offset
.col
;
372 dat
->rinfo
.pos
.eval
.row
= dat
->cr
->base
.row
+ src
->offset
.row
;
374 dat
->rinfo
.pos
.eval
.col
= target_col
;
375 dat
->rinfo
.pos
.eval
.row
= target_row
;
378 paste_cell (target_col
, target_row
, src
, dat
);
382 range_flip_h (GnmRange
*range
, Sheet
const *sheet
, int const *data
)
386 g_return_val_if_fail (range
!= NULL
, TRUE
);
388 t
= *data
- range
->end
.col
;
389 range
->end
.col
= *data
- range
->start
.col
;
390 range
->start
.col
= t
;
395 range_flip_v (GnmRange
*range
, Sheet
const *sheet
, int const *data
)
399 g_return_val_if_fail (range
!= NULL
, TRUE
);
401 t
= *data
- range
->end
.row
;
402 range
->end
.row
= *data
- range
->start
.row
;
403 range
->start
.row
= t
;
409 * clipboard_paste_region:
410 * @cr: The GnmCellRegion to paste.
411 * @pt: Where to paste the values.
412 * @cc: (nullable): The context for error handling.
414 * Pastes the supplied GnmCellRegion (@cr) into the supplied
415 * GnmPasteTarget (@pt). This operation is not undoable. It does not auto grow
416 * the destination if the target is a singleton. This is a simple interface to
419 * Returns: %TRUE if there was a problem.
422 clipboard_paste_region (GnmCellRegion
const *cr
,
423 GnmPasteTarget
const *pt
,
426 int repeat_horizontal
, repeat_vertical
, clearFlags
;
427 int dst_cols
, dst_rows
, src_cols
, src_rows
;
431 gboolean has_contents
, adjust_merges
= TRUE
;
432 struct paste_cell_data dat
;
433 GnmRange
const *merge_src
;
434 gboolean no_flipping
, do_col_widths
, do_row_heights
;
436 g_return_val_if_fail (pt
!= NULL
, TRUE
);
437 g_return_val_if_fail (cr
!= NULL
, TRUE
);
439 /* we do not need any of this fancy stuff when pasting a simple object */
440 if (cr
->cell_content
== NULL
&&
441 cr
->styles
== NULL
&&
442 cr
->merged
== NULL
&&
443 cr
->objects
!= NULL
) {
444 if (pt
->paste_flags
& (PASTE_COMMENTS
| PASTE_OBJECTS
))
445 for (ptr
= cr
->objects
; ptr
; ptr
= ptr
->next
)
446 paste_object (pt
, ptr
->data
,
447 pt
->range
.start
.col
, pt
->range
.start
.row
);
452 dst_cols
= range_width (r
);
453 dst_rows
= range_height (r
);
457 /* If the source is a single cell or a single merge */
458 /* Treat a target of a single merge specially, don't split the merge */
459 if ((src_cols
== 1 && src_rows
== 1) ||
460 (g_slist_length (cr
->merged
) == 1 &&
461 (NULL
!= (merge_src
= cr
->merged
->data
)) &&
462 range_height (merge_src
) == cr
->rows
&&
463 range_width (merge_src
) == cr
->cols
)) {
464 GnmRange
const *merge
= gnm_sheet_merge_is_corner (pt
->sheet
, &r
->start
);
465 if (merge
!= NULL
&& range_equal (r
, merge
)) {
466 dst_cols
= dst_rows
= 1;
467 adjust_merges
= FALSE
;
471 /* Apparently links do not supercede merges */
472 } else if (pt
->paste_flags
& PASTE_LINK
)
473 adjust_merges
= FALSE
;
475 has_contents
= pt
->paste_flags
& (PASTE_CONTENTS
|PASTE_AS_VALUES
|PASTE_LINK
);
477 if (pt
->paste_flags
& PASTE_TRANSPOSE
) {
483 if (cr
->not_as_contents
&& (pt
->paste_flags
& PASTE_CONTENTS
)) {
485 go_cmd_context_error_invalid
487 _("Unable to paste"),
488 _("Contents can only be pasted by value or by link."));
492 /* calculate the tiling */
493 repeat_horizontal
= dst_cols
/src_cols
;
494 if (repeat_horizontal
* src_cols
!= dst_cols
) {
495 char *msg
= g_strdup_printf (
496 _("destination does not have an even multiple of source columns (%d vs %d)\n\n"
497 "Try selecting a single cell or an area of the same shape and size."),
500 go_cmd_context_error_invalid (cc
, _("Unable to paste"), msg
);
505 repeat_vertical
= dst_rows
/src_rows
;
506 if (repeat_vertical
* src_rows
!= dst_rows
) {
507 char *msg
= g_strdup_printf (
508 _("destination does not have an even multiple of source rows (%d vs %d)\n\n"
509 "Try selecting a single cell or an area of the same shape and size."),
512 go_cmd_context_error_invalid (cc
, _("Unable to paste"), msg
);
517 if ((pt
->range
.start
.col
+ dst_cols
) > gnm_sheet_get_max_cols (pt
->sheet
) ||
518 (pt
->range
.start
.row
+ dst_rows
) > gnm_sheet_get_max_rows (pt
->sheet
)) {
520 go_cmd_context_error_invalid
522 _("Unable to paste"),
523 _("result passes the sheet boundary"));
528 /* clear the region where we will paste */
530 clearFlags
= CLEAR_VALUES
| CLEAR_NORESPAN
;
532 if (pt
->paste_flags
& PASTE_COMMENTS
)
533 clearFlags
|= CLEAR_COMMENTS
;
535 /* No need to clear the formats. We will paste over top of these. */
536 /* if (pt->paste_flags & PASTE_FORMATS) clearFlags |= CLEAR_FORMATS; */
538 if (pt
->paste_flags
& (PASTE_OPER_MASK
| PASTE_SKIP_BLANKS
))
541 /* remove merged regions even for operations, or blanks */
542 if (has_contents
&& adjust_merges
)
543 clearFlags
|= CLEAR_MERGES
;
545 if (clearFlags
!= 0) {
546 int const dst_col
= pt
->range
.start
.col
;
547 int const dst_row
= pt
->range
.start
.row
;
548 sheet_clear_region (pt
->sheet
,
550 dst_col
+ dst_cols
- 1,
551 dst_row
+ dst_rows
- 1,
555 dat
.translate_dates
= cr
->date_conv
&&
556 !go_date_conv_equal (cr
->date_conv
, sheet_date_conv (pt
->sheet
));
558 for (i
= 0; i
< repeat_horizontal
; i
++)
559 for (j
= 0; j
< repeat_vertical
; j
++) {
560 int const left
= i
* src_cols
+ pt
->range
.start
.col
;
561 int const top
= j
* src_rows
+ pt
->range
.start
.row
;
563 dat
.top_left
.col
= left
;
564 dat
.top_left
.row
= top
;
565 dat
.rinfo
.reloc_type
= GNM_EXPR_RELOCATE_MOVE_RANGE
;
566 dat
.rinfo
.origin_sheet
= dat
.rinfo
.target_sheet
= pt
->sheet
;
567 if (pt
->paste_flags
& PASTE_EXPR_LOCAL_RELOCATE
) {
568 dat
.rinfo
.origin
.start
= cr
->base
;
569 dat
.rinfo
.origin
.end
.col
= cr
->base
.col
+ cr
->cols
- 1;
570 dat
.rinfo
.origin
.end
.row
= cr
->base
.row
+ cr
->rows
- 1;
571 dat
.rinfo
.col_offset
= left
- cr
->base
.col
;
572 dat
.rinfo
.row_offset
= top
- cr
->base
.row
;
574 dat
.rinfo
.origin
= pt
->range
;
575 dat
.rinfo
.col_offset
= 0;
576 dat
.rinfo
.row_offset
= 0;
579 /* Move the styles on here so we get correct formats before recalc */
580 if (pt
->paste_flags
& PASTE_FORMATS
) {
581 if (pt
->paste_flags
& PASTE_TRANSPOSE
)
582 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
584 (sheet_style_set_list_cb_t
)
587 else if (pt
->paste_flags
& PASTE_FLIP_H
) {
588 int data
= 2 * left
+ src_cols
- 1;
589 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
591 (sheet_style_set_list_cb_t
)
592 range_flip_h
, &data
);
593 } else if (pt
->paste_flags
& PASTE_FLIP_V
) {
594 int data
= 2 * top
+ src_rows
- 1;
595 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
597 (sheet_style_set_list_cb_t
)
598 range_flip_v
, &data
);
600 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
601 cr
->styles
, NULL
, NULL
);
603 if (has_contents
&& !(pt
->paste_flags
& PASTE_DONT_MERGE
)) {
604 for (ptr
= cr
->merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
605 GnmRange tmp
= *((GnmRange
const *)ptr
->data
);
606 if (pt
->paste_flags
& PASTE_TRANSPOSE
) {
608 x
= tmp
.start
.col
; tmp
.start
.col
= tmp
.start
.row
; tmp
.start
.row
= x
;
609 x
= tmp
.end
.col
; tmp
.end
.col
= tmp
.end
.row
; tmp
.end
.row
= x
;
611 if (!range_translate (&tmp
, pt
->sheet
, left
, top
))
612 gnm_sheet_merge_add (pt
->sheet
, &tmp
, TRUE
, cc
);
616 if (has_contents
&& (pt
->paste_flags
& PASTE_LINK
)) {
617 paste_link (pt
, top
, left
, cr
);
621 if (has_contents
&& NULL
!= cr
->cell_content
) {
624 g_hash_table_foreach (cr
->cell_content
,
625 (GHFunc
)cb_paste_cell
, &dat
);
628 if (pt
->paste_flags
& (PASTE_COMMENTS
| PASTE_OBJECTS
))
629 for (ptr
= cr
->objects
; ptr
; ptr
= ptr
->next
)
630 paste_object (pt
, ptr
->data
, left
, top
);
633 no_flipping
= (pt
->paste_flags
& (PASTE_FLIP_H
| PASTE_FLIP_V
| PASTE_TRANSPOSE
)) == 0;
636 ((pt
->paste_flags
& PASTE_COLUMN_WIDTHS
) ||
637 ((pt
->paste_flags
& PASTE_COLUMN_WIDTHS_AUTO
) &&
639 src_rows
== gnm_sheet_get_max_rows (cr
->origin_sheet
)));
642 for (i
= 0; i
< repeat_horizontal
; i
++) {
643 int first
= pt
->range
.start
.col
+ i
* src_cols
;
644 colrow_set_states (pt
->sheet
, TRUE
, first
, cr
->col_state
);
650 ((pt
->paste_flags
& PASTE_ROW_HEIGHTS
) ||
651 ((pt
->paste_flags
& PASTE_ROW_HEIGHTS_AUTO
) &&
653 src_cols
== gnm_sheet_get_max_cols (cr
->origin_sheet
)));
654 if (do_row_heights
) {
656 for (i
= 0; i
< repeat_vertical
; i
++) {
657 int first
= pt
->range
.start
.row
+ i
* src_rows
;
658 colrow_set_states (pt
->sheet
, FALSE
, first
, cr
->row_state
);
662 if (!(pt
->paste_flags
& PASTE_NO_RECALC
)) {
664 sheet_region_queue_recalc (pt
->sheet
, r
);
665 sheet_flag_status_update_range (pt
->sheet
, r
);
667 sheet_flag_style_update_range (pt
->sheet
, r
);
669 sheet_range_calc_spans (pt
->sheet
, r
,
670 (pt
->paste_flags
& PASTE_FORMATS
) ? GNM_SPANCALC_RE_RENDER
: GNM_SPANCALC_RENDER
);
671 sheet_redraw_all (pt
->sheet
, FALSE
);
678 cb_clipboard_prepend_cell (GnmCellIter
const *iter
, GnmCellRegion
*cr
)
681 GnmCellCopy
*copy
= gnm_cell_copy_new (cr
,
682 iter
->pp
.eval
.col
- cr
->base
.col
,
683 iter
->pp
.eval
.row
- cr
->base
.row
);
684 copy
->val
= value_dup (iter
->cell
->value
);
686 if (gnm_cell_has_expr (iter
->cell
)) {
687 gnm_expr_top_ref (copy
->texpr
= iter
->cell
->base
.texpr
);
689 /* Check for array division */
690 if (!cr
->not_as_contents
&&
691 gnm_cell_array_bound (iter
->cell
, &a
) &&
692 (a
.start
.col
< cr
->base
.col
||
693 a
.start
.row
< cr
->base
.row
||
694 a
.end
.col
>= (cr
->base
.col
+ cr
->cols
) ||
695 a
.end
.row
>= (cr
->base
.row
+ cr
->rows
)))
696 cr
->not_as_contents
= TRUE
;
704 cb_dup_objects (SheetObject
const *src
, GnmCellRegion
*cr
)
706 SheetObject
*dst
= sheet_object_dup (src
);
708 SheetObjectAnchor tmp
= *sheet_object_get_anchor (src
);
709 range_translate (&tmp
.cell_bound
, sheet_object_get_sheet (src
),
710 - cr
->base
.col
, - cr
->base
.row
);
711 sheet_object_set_anchor (dst
, &tmp
);
712 cr
->objects
= g_slist_prepend (cr
->objects
, dst
);
717 * clipboard_copy_range:
719 * Entry point to the clipboard copy code
722 clipboard_copy_range (Sheet
*sheet
, GnmRange
const *r
)
725 GSList
*merged
, *ptr
;
728 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
729 g_return_val_if_fail (range_is_sane (r
), NULL
);
731 cr
= gnm_cell_region_new (sheet
);
733 cr
->cols
= range_width (r
);
734 cr
->rows
= range_height (r
);
735 cr
->col_state
= colrow_get_states (sheet
,
736 TRUE
, r
->start
.col
, r
->end
.col
);
737 cr
->row_state
= colrow_get_states (sheet
,
738 FALSE
, r
->start
.row
, r
->end
.row
);
740 sheet_foreach_cell_in_range ( sheet
, CELL_ITER_IGNORE_NONEXISTENT
, r
,
741 (CellIterFunc
) cb_clipboard_prepend_cell
,
743 objects
= sheet_objects_get (sheet
, r
, G_TYPE_NONE
);
744 g_slist_foreach (objects
, (GFunc
)cb_dup_objects
, cr
);
745 g_slist_free (objects
);
747 cr
->styles
= sheet_style_get_range (sheet
, r
);
749 merged
= gnm_sheet_merge_get_overlap (sheet
, r
);
750 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
751 GnmRange
*tmp
= gnm_range_dup (ptr
->data
);
752 range_translate (tmp
, sheet
, -r
->start
.col
, -r
->start
.row
);
753 cr
->merged
= g_slist_prepend (cr
->merged
, tmp
);
755 g_slist_free (merged
);
761 cb_clipboard_copy_range_undo (GnmCellRegion
*cr
, GnmSheetRange
*sr
,
766 clipboard_paste_region
768 paste_target_init (&pt
,
771 PASTE_CONTENTS
| PASTE_FORMATS
|
772 PASTE_OBJECTS
| PASTE_COMMENTS
|
773 PASTE_COLUMN_WIDTHS
| PASTE_ROW_HEIGHTS
),
778 * clipboard_copy_range_undo:
782 * Returns: (transfer full): A #GOUndo object that will restore the contents
783 * of the given range.
786 clipboard_copy_range_undo (Sheet
*sheet
, GnmRange
const *r
)
788 GnmCellRegion
*cr
= clipboard_copy_range (sheet
, r
);
789 g_return_val_if_fail (cr
!= NULL
, NULL
);
790 return go_undo_binary_new (cr
, gnm_sheet_range_new (sheet
, r
),
791 (GOUndoBinaryFunc
)cb_clipboard_copy_range_undo
,
792 (GFreeFunc
)cellregion_unref
,
797 * clipboard_copy_ranges_undo:
799 * @ranges: (element-type GnmRange) (transfer none): list of ranges
801 * Returns: (transfer full): A #GOUndo object that will restore the contents
802 * of the given range.
805 clipboard_copy_ranges_undo (Sheet
*sheet
, GSList
*ranges
)
810 for (l
= ranges
; l
!= NULL
; l
= l
->next
) {
811 GnmRange
*r
= l
->data
;
812 GOUndo
*undo1
= clipboard_copy_range_undo (sheet
, r
);
813 undo
= go_undo_combine (undo
, undo1
);
821 * clipboard_copy_obj:
823 * @objects: (element-type SheetObject): #GSList
825 * Returns a cell region with copies of objects in list. Caller is responsible
826 * for cellregion_unref-ing the result.
829 clipboard_copy_obj (Sheet
*sheet
, GSList
*objects
)
831 SheetObjectAnchor tmp_anchor
;
832 SheetObjectAnchor
const *anchor
;
840 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
841 g_return_val_if_fail (objects
!= NULL
, NULL
);
843 cr
= gnm_cell_region_new (sheet
);
844 for (ptr
= objects
; ptr
!= NULL
; ptr
= ptr
->next
)
845 if (NULL
!= (so
= sheet_object_dup (ptr
->data
))) {
846 anchor
= sheet_object_get_anchor (so
);
848 #warning FIXME : This is only used in gnm_sog_write_image
849 /* NOTE #1 : It seems necessary to handle pasting an object that has been removed from
850 * the sheet after being added to the clipboard. it seems like we would need
851 * this sort of information for anything that implements SheetObjectImageableIface
853 sheet_object_anchor_to_pts (anchor
, sheet
, coords
);
854 w
= fabs (coords
[2] - coords
[0]) + 1.5;
855 h
= fabs (coords
[3] - coords
[1]) + 1.5;
856 g_object_set_data (G_OBJECT (so
), "pt-width-at-copy",
857 GUINT_TO_POINTER (w
));
858 g_object_set_data (G_OBJECT (so
), "pt-height-at-copy",
859 GUINT_TO_POINTER (h
));
861 tmp_anchor
= *anchor
;
862 r
= &tmp_anchor
.cell_bound
;
863 range_translate (r
, sheet
,
864 -MIN (r
->start
.col
, r
->end
.col
),
865 -MIN (r
->start
.row
, r
->end
.row
));
866 sheet_object_set_anchor (so
, &tmp_anchor
);
868 cr
->objects
= g_slist_prepend (cr
->objects
, so
);
875 paste_target_init (GnmPasteTarget
*pt
, Sheet
*sheet
,
876 GnmRange
const *r
, GnmPasteFlags flags
)
878 pt
->sheet
= sheet
; // No ref
880 pt
->paste_flags
= flags
;
885 * gnm_cell_region_new:
886 * @origin_sheet: optionally NULL.
888 * A convenience routine to create CellRegions and init the flags nicely.
891 gnm_cell_region_new (Sheet
*origin_sheet
)
893 GnmCellRegion
*cr
= g_new0 (GnmCellRegion
, 1);
894 cr
->origin_sheet
= origin_sheet
;
895 cr
->date_conv
= origin_sheet
&& origin_sheet
->workbook
896 ? sheet_date_conv (origin_sheet
)
898 cr
->cols
= cr
->rows
= -1;
899 cr
->not_as_contents
= FALSE
;
900 cr
->cell_content
= NULL
;
901 cr
->col_state
= NULL
;
902 cr
->row_state
= NULL
;
912 cellregion_ref (GnmCellRegion
*cr
)
914 g_return_val_if_fail (cr
!= NULL
, NULL
);
920 cellregion_unref (GnmCellRegion
*cr
)
922 g_return_if_fail (cr
!= NULL
);
924 if (cr
->ref_count
> 1) {
929 if (NULL
!= cr
->cell_content
) {
930 g_hash_table_destroy (cr
->cell_content
);
931 cr
->cell_content
= NULL
;
934 if (NULL
!= cr
->col_state
)
935 cr
->col_state
= colrow_state_list_destroy (cr
->col_state
);
936 if (NULL
!= cr
->row_state
)
937 cr
->row_state
= colrow_state_list_destroy (cr
->row_state
);
938 if (cr
->styles
!= NULL
) {
939 style_list_free (cr
->styles
);
942 if (cr
->merged
!= NULL
) {
944 for (ptr
= cr
->merged
; ptr
!= NULL
; ptr
= ptr
->next
)
946 g_slist_free (cr
->merged
);
949 if (cr
->objects
!= NULL
) {
951 for (ptr
= cr
->objects
; ptr
!= NULL
; ptr
= ptr
->next
)
952 g_object_unref (ptr
->data
);
953 g_slist_free (cr
->objects
);
961 gnm_cell_region_get_type (void)
966 t
= g_boxed_type_register_static ("GnmCellRegion",
967 (GBoxedCopyFunc
)cellregion_ref
,
968 (GBoxedFreeFunc
)cellregion_unref
);
974 cellregion_get_content (GnmCellRegion
const *cr
, int col
, int row
)
976 if (cr
->cell_content
) {
980 return g_hash_table_lookup (cr
->cell_content
, &pos
);
986 cb_invalidate_cellcopy (GnmCellCopy
*cc
, gconstpointer ignore
,
987 GnmExprRelocateInfo
*rinfo
)
989 GnmExprTop
const *texpr
;
990 if (NULL
!= cc
->texpr
) {
991 texpr
= gnm_expr_top_relocate (cc
->texpr
, rinfo
, FALSE
);
993 gnm_expr_top_unref (cc
->texpr
);
1000 * cellregion_invalidate_sheet:
1001 * @cr: #GnmCellRegion
1004 * Invalidate references from cell content, objects or style to @sheet.
1007 cellregion_invalidate_sheet (GnmCellRegion
*cr
,
1011 gboolean save_invalidated
;
1012 GnmExprRelocateInfo rinfo
;
1014 g_return_if_fail (cr
!= NULL
);
1015 g_return_if_fail (IS_SHEET (sheet
));
1017 save_invalidated
= sheet
->being_invalidated
;
1018 sheet
->being_invalidated
= TRUE
;
1020 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_INVALIDATE_SHEET
;
1021 if (NULL
!= cr
->cell_content
)
1022 g_hash_table_foreach (cr
->cell_content
,
1023 (GHFunc
)cb_invalidate_cellcopy
, &rinfo
);
1024 sheet
->being_invalidated
= save_invalidated
;
1026 for (ptr
= cr
->objects
; ptr
!= NULL
; ptr
= ptr
->next
)
1027 sheet_object_invalidate_sheet (ptr
->data
, sheet
);
1029 if (cr
->origin_sheet
== sheet
)
1030 cr
->origin_sheet
= NULL
;
1034 cb_cellregion_extent (GnmCellCopy
*cc
, gconstpointer ignore
, GnmRange
*extent
)
1036 if (extent
->start
.col
>= 0) {
1037 if (extent
->start
.col
> cc
->offset
.col
)
1038 extent
->start
.col
= cc
->offset
.col
;
1039 else if (extent
->end
.col
< cc
->offset
.col
)
1040 extent
->end
.col
= cc
->offset
.col
;
1042 if (extent
->start
.row
> cc
->offset
.row
)
1043 extent
->start
.row
= cc
->offset
.row
;
1044 else if (extent
->end
.row
< cc
->offset
.row
)
1045 extent
->end
.row
= cc
->offset
.row
;
1046 } else /* first cell */
1047 extent
->start
= extent
->end
= cc
->offset
;
1051 * cellregion_extent:
1052 * @cr: #GnmCellRegion
1053 * @extent: #GnmRange
1055 * Find the min and max col/row with cell content
1058 cellregion_extent (GnmCellRegion
const *cr
, GnmRange
*extent
)
1060 if (NULL
!= cr
->cell_content
) {
1061 range_init (extent
, -1, -1, -1, -1);
1062 g_hash_table_foreach (cr
->cell_content
,
1063 (GHFunc
)cb_cellregion_extent
, extent
);
1065 range_init (extent
, 0, 0, 0, 0);
1069 cellregion_to_string (GnmCellRegion
const *cr
,
1070 gboolean only_visible
,
1071 GODateConventions
const *date_conv
)
1073 GString
*all
, *line
;
1074 GnmCellCopy
const *cc
;
1075 int col
, row
, next_col_check
, next_row_check
;
1077 ColRowStateList
const *col_state
= NULL
, *row_state
= NULL
;
1078 ColRowRLEState
const *rle
;
1080 GnmStyle
const *style
;
1081 GOFormat
const *fmt
;
1083 g_return_val_if_fail (cr
!= NULL
, NULL
);
1084 g_return_val_if_fail (cr
->rows
>= 0, NULL
);
1085 g_return_val_if_fail (cr
->cols
>= 0, NULL
);
1087 /* pre-allocate rough approximation of buffer */
1088 ncells
= cr
->cell_content
? g_hash_table_size (cr
->cell_content
) : 0;
1089 all
= g_string_sized_new (20 * ncells
+ 1);
1090 line
= g_string_new (NULL
);
1092 cellregion_extent (cr
, &extent
);
1094 if (only_visible
&& NULL
!= (row_state
= cr
->row_state
)) {
1095 next_row_check
= i
= 0;
1096 while ((i
+= ((ColRowRLEState
*)(row_state
->data
))->length
) <= extent
.start
.row
) {
1097 if (NULL
== (row_state
= row_state
->next
)) {
1098 next_row_check
= gnm_sheet_get_max_rows (cr
->origin_sheet
);
1104 next_row_check
= gnm_sheet_get_max_rows (cr
->origin_sheet
);
1106 for (row
= extent
.start
.row
; row
<= extent
.end
.row
;) {
1107 if (row
>= next_row_check
) {
1108 rle
= row_state
->data
;
1109 row_state
= row_state
->next
;
1110 next_row_check
+= rle
->length
;
1111 if (!rle
->state
.visible
) {
1112 row
= next_row_check
;
1117 g_string_assign (line
, "");
1119 if (only_visible
&& NULL
!= (col_state
= cr
->col_state
)) {
1120 next_col_check
= i
= 0;
1121 while ((i
+= ((ColRowRLEState
*)(col_state
->data
))->length
) <= extent
.start
.col
) {
1122 if (NULL
== (col_state
= col_state
->next
)) {
1123 next_col_check
= gnm_sheet_get_max_cols (cr
->origin_sheet
);
1129 next_col_check
= gnm_sheet_get_max_cols (cr
->origin_sheet
);
1131 for (col
= extent
.start
.col
; col
<= extent
.end
.col
;) {
1132 if (col
== next_col_check
) {
1133 rle
= col_state
->data
;
1134 col_state
= col_state
->next
;
1135 next_col_check
+= rle
->length
;
1136 if (!rle
->state
.visible
) {
1137 col
= next_col_check
;
1142 cc
= cellregion_get_content (cr
, col
, row
);
1144 style
= style_list_get_style (cr
->styles
, col
, row
);
1145 fmt
= gnm_style_get_format (style
);
1147 if (go_format_is_general (fmt
) &&
1148 VALUE_FMT (cc
->val
))
1149 fmt
= VALUE_FMT (cc
->val
);
1151 format_value_gstring (line
, fmt
, cc
->val
,
1154 if (++col
<= extent
.end
.col
)
1155 g_string_append_c (line
, '\t');
1157 g_string_append_len (all
, line
->str
, line
->len
);
1158 if (++row
<= extent
.end
.row
)
1159 g_string_append_c (all
, '\n');
1162 g_string_free (line
, TRUE
);
1167 cellregion_cmd_size (GnmCellRegion
const *cr
)
1171 g_return_val_if_fail (cr
!= NULL
, 1);
1173 res
+= g_slist_length (cr
->styles
);
1174 if (NULL
!= cr
->cell_content
)
1175 res
+= g_hash_table_size (cr
->cell_content
);
1180 gnm_cell_copy_free (GnmCellCopy
*cc
)
1183 gnm_expr_top_unref (cc
->texpr
);
1186 value_release (cc
->val
);
1189 CHUNK_FREE (cell_copy_pool
, cc
);
1193 gnm_cell_copy_new (GnmCellRegion
*cr
, int col_offset
, int row_offset
)
1195 GnmCellCopy
*res
= CHUNK_ALLOC (GnmCellCopy
, cell_copy_pool
);
1196 ((GnmCellPos
*)(&res
->offset
))->col
= col_offset
;
1197 ((GnmCellPos
*)(&res
->offset
))->row
= row_offset
;
1201 if (NULL
== cr
->cell_content
)
1202 cr
->cell_content
= g_hash_table_new_full (
1203 (GHashFunc
)&gnm_cellpos_hash
,
1204 (GCompareFunc
)&gnm_cellpos_equal
,
1205 (GDestroyNotify
) gnm_cell_copy_free
,
1208 g_hash_table_insert (cr
->cell_content
, res
, res
);
1214 * clipboard_init: (skip)
1217 clipboard_init (void)
1219 #if USE_CELL_COPY_POOLS
1221 go_mem_chunk_new ("cell copy pool",
1222 sizeof (GnmCellCopy
),
1227 #if USE_CELL_COPY_POOLS
1229 cb_cell_copy_pool_leak (gpointer data
, G_GNUC_UNUSED gpointer user
)
1231 GnmCellCopy
const *cc
= data
;
1232 g_printerr ("Leaking cell copy at %p.\n", (void *)cc
);
1237 * clipboard_shutdown: (skip)
1240 clipboard_shutdown (void)
1242 #if USE_CELL_COPY_POOLS
1243 go_mem_chunk_foreach_leak (cell_copy_pool
, cb_cell_copy_pool_leak
, NULL
);
1244 go_mem_chunk_destroy (cell_copy_pool
, FALSE
);
1245 cell_copy_pool
= NULL
;