1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * clipboard.c: A temporary store for contents from a worksheet
5 * Copyright (C) 2000-2008 Jody Goldberg (jody@gnome.org)
6 * 1999 Miguel de Icaza (miguel@gnu.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 #include <gnumeric-config.h>
25 #include "clipboard.h"
29 #include "sheet-style.h"
30 #include "sheet-merge.h"
31 #include "dependent.h"
32 #include "selection.h"
33 #include "command-context.h"
34 #include "workbook-control.h"
41 #include "stf-parse.h"
42 #include "gnm-format.h"
43 #include "sheet-object-cell-comment.h"
45 #include <glib/gi18n-lib.h>
48 #include <goffice/goffice.h>
50 #ifndef USE_CELL_COPY_POOLS
51 #define USE_CELL_COPY_POOLS 1
54 #if USE_CELL_COPY_POOLS
55 /* Memory pool for GnmCellCopy. */
56 static GOMemChunk
*cell_copy_pool
;
57 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
58 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
60 #define CHUNK_ALLOC(T,c) g_new (T,1)
61 #define CHUNK_FREE(p,v) g_free ((v))
64 /* creating a boxed type for GnmCellCopy (needed by introspection) */
66 pointer_dup (gpointer
*cc
)
72 gnm_cell_copy_get_type (void)
77 t
= g_boxed_type_register_static ("GnmCellCopy",
78 (GBoxedCopyFunc
)pointer_dup
,
79 (GBoxedFreeFunc
)pointer_dup
);
84 /* creating a boxed type for GnmPasteTarget (needed by introspection) */
86 static GnmPasteTarget
*
87 gnm_paste_target_copy (GnmPasteTarget
*pt
)
89 return g_memdup (pt
, sizeof (*pt
));
93 gnm_paste_target_get_type (void)
98 t
= g_boxed_type_register_static ("GnmPasteTarget",
99 (GBoxedCopyFunc
)gnm_paste_target_copy
,
100 (GBoxedFreeFunc
)g_free
);
106 gnm_paste_target_new (Sheet
*sheet
, GnmRange
*r
, GnmPasteFlags flags
)
108 GnmPasteTarget
*res
= g_new (GnmPasteTarget
, 1);
109 paste_target_init (res
, sheet
, r
, flags
);
115 cell_has_expr_or_number_or_blank (GnmCell
const * cell
)
117 return (gnm_cell_is_empty (cell
) ||
118 (cell
!= NULL
&& gnm_cell_is_number (cell
)) ||
119 (cell
!= NULL
&& gnm_cell_has_expr (cell
)));
122 static GnmExpr
const *
123 contents_as_expr (GnmExprTop
const *texpr
, GnmValue
const *val
)
126 return gnm_expr_copy (texpr
->expr
);
127 if (VALUE_IS_EMPTY (val
))
128 return gnm_expr_new_constant (value_new_float (0.0));
129 if (VALUE_IS_NUMBER (val
))
130 return gnm_expr_new_constant (value_dup (val
));
135 paste_op_to_expr_op (int paste_flags
)
137 g_return_val_if_fail (paste_flags
& PASTE_OPER_MASK
, 0);
139 if (paste_flags
& PASTE_OPER_ADD
)
140 return GNM_EXPR_OP_ADD
;
141 else if (paste_flags
& PASTE_OPER_SUB
)
142 return GNM_EXPR_OP_SUB
;
143 else if (paste_flags
& PASTE_OPER_MULT
)
144 return GNM_EXPR_OP_MULT
;
145 else if (paste_flags
& PASTE_OPER_DIV
)
146 return GNM_EXPR_OP_DIV
;
152 paste_cell_with_operation (Sheet
*dst_sheet
,
153 int target_col
, int target_row
,
154 GnmExprRelocateInfo
const *rinfo
,
155 GnmCellCopy
const *src
,
161 if (src
->texpr
== NULL
&&
162 !VALUE_IS_EMPTY (src
->val
) &&
163 !VALUE_IS_NUMBER (src
->val
))
166 dst
= sheet_cell_fetch (dst_sheet
, target_col
, target_row
);
167 if (!cell_has_expr_or_number_or_blank (dst
))
170 op
= paste_op_to_expr_op (paste_flags
);
171 /* FIXME : This does not handle arrays, linked cells, ranges, etc. */
172 if ((paste_flags
& PASTE_CONTENTS
) &&
173 (NULL
!= src
->texpr
|| gnm_cell_has_expr (dst
))) {
174 GnmExpr
const *old_expr
= contents_as_expr (dst
->base
.texpr
, dst
->value
);
175 GnmExpr
const *copied_expr
= contents_as_expr (src
->texpr
, src
->val
);
176 GnmExprTop
const *res
= gnm_expr_top_new (gnm_expr_new_binary (old_expr
, op
, copied_expr
));
177 GnmExprTop
const *relo
= gnm_expr_top_relocate (res
, rinfo
, FALSE
);
179 gnm_cell_set_expr (dst
, relo
);
180 gnm_expr_top_unref (relo
);
182 gnm_cell_set_expr (dst
, res
);
183 gnm_expr_top_unref (res
);
187 GnmExpr
const *expr
= gnm_expr_new_binary (
188 gnm_expr_new_constant (value_dup (dst
->value
)),
190 gnm_expr_new_constant (value_dup (src
->val
)));
191 GnmExprTop
const *texpr
= gnm_expr_top_new (expr
);
193 eval_pos_init_cell (&pos
, dst
);
194 pos
.dep
= NULL
; /* no dynamic deps */
195 value
= gnm_expr_top_eval (texpr
, &pos
,
196 GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
197 gnm_expr_top_unref (texpr
);
198 gnm_cell_set_value (dst
, value
);
202 /* NOTE : Make sure to set up any merged regions in the target range BEFORE
206 paste_link (GnmPasteTarget
const *pt
, int top
, int left
,
207 GnmCellRegion
const *cr
)
210 GnmCellRef source_cell_ref
;
213 /* Not possible to link to arbitrary (non gnumeric) sources yet. */
214 /* TODO : eventually support interprocess gnumeric links */
215 if (cr
->origin_sheet
== NULL
)
218 /* TODO : support relative links ? */
219 source_cell_ref
.col_relative
= 0;
220 source_cell_ref
.row_relative
= 0;
221 source_cell_ref
.sheet
= (cr
->origin_sheet
!= pt
->sheet
)
222 ? cr
->origin_sheet
: NULL
;
224 for (x
= 0 ; x
< cr
->cols
; x
++, pos
.col
++) {
225 source_cell_ref
.col
= cr
->base
.col
+ x
;
227 for (y
= 0 ; y
< cr
->rows
; y
++, pos
.row
++) {
228 GnmExprTop
const *texpr
;
230 sheet_cell_fetch (pt
->sheet
, pos
.col
, pos
.row
);
232 /* This could easily be made smarter */
233 if (!gnm_cell_is_merged (cell
) &&
234 gnm_sheet_merge_contains_pos (pt
->sheet
, &pos
))
236 source_cell_ref
.row
= cr
->base
.row
+ y
;
237 texpr
= gnm_expr_top_new (gnm_expr_new_cellref (&source_cell_ref
));
238 gnm_cell_set_expr (cell
, texpr
);
239 gnm_expr_top_unref (texpr
);
244 struct paste_cell_data
{
245 GnmPasteTarget
const *pt
;
246 GnmCellRegion
const *cr
;
248 GnmExprRelocateInfo rinfo
;
249 gboolean translate_dates
;
254 * @target_col: Column to put the cell into
255 * @target_row: Row to put the cell into.
256 * @src: A #GnmCellCopy with the content to paste
257 * @paste_flags: Bit mask that describes the paste options.
259 * Pastes a cell in the spreadsheet.
262 paste_cell (int target_col
, int target_row
,
263 GnmCellCopy
const *src
,
264 const struct paste_cell_data
*dat
)
266 Sheet
*dst_sheet
= dat
->pt
->sheet
;
267 int paste_flags
= dat
->pt
->paste_flags
;
269 if (paste_flags
& PASTE_OPER_MASK
)
270 paste_cell_with_operation (dst_sheet
, target_col
, target_row
,
271 &dat
->rinfo
, src
, paste_flags
);
273 GnmCell
*dst
= sheet_cell_fetch (dst_sheet
, target_col
, target_row
);
274 if (NULL
!= src
->texpr
&& (paste_flags
& PASTE_CONTENTS
)) {
275 GnmExprTop
const *relo
= gnm_expr_top_relocate (
276 src
->texpr
, &dat
->rinfo
, FALSE
);
277 if (paste_flags
& PASTE_TRANSPOSE
) {
278 GnmExprTop
const *trelo
=
279 gnm_expr_top_transpose (relo
? relo
: src
->texpr
);
282 gnm_expr_top_unref (relo
);
285 } else if (!relo
&& gnm_expr_top_is_array_corner (src
->texpr
)) {
286 /* We must not share array expressions. */
287 relo
= gnm_expr_top_new (gnm_expr_copy (src
->texpr
->expr
));
289 gnm_cell_set_expr_and_value (dst
, relo
? relo
: src
->texpr
,
290 value_dup (src
->val
), TRUE
);
292 gnm_expr_top_unref (relo
);
293 } else if (src
->val
) {
294 GnmValue
*newval
= NULL
;
295 GnmValue
const *oldval
= src
->val
;
297 if (dat
->translate_dates
&& oldval
&& VALUE_IS_FLOAT (oldval
)) {
298 GOFormat
const *fmt
= VALUE_FMT (oldval
)
300 : gnm_style_get_format (gnm_cell_get_style (dst
));
301 if (go_format_is_date (fmt
) > 0) {
302 gnm_float fnew
= go_date_conv_translate
303 (value_get_as_float (oldval
),
305 sheet_date_conv (dst_sheet
));
306 newval
= value_new_float (fnew
);
307 value_set_fmt (newval
, VALUE_FMT (oldval
));
312 newval
= value_dup (src
->val
);
313 gnm_cell_set_value (dst
, newval
);
319 paste_object (GnmPasteTarget
const *pt
, SheetObject
const *src
, int left
, int top
)
322 SheetObjectAnchor tmp
;
324 tmp
= *sheet_object_get_anchor (src
);
325 if (G_OBJECT_TYPE (src
) == GNM_CELL_COMMENT_TYPE
) {
326 if ((pt
->paste_flags
& PASTE_COMMENTS
) &&
327 (pt
->paste_flags
& PASTE_IGNORE_COMMENTS_AT_ORIGIN
&&
328 tmp
.cell_bound
.start
.col
== 0 &&
329 tmp
.cell_bound
.start
.row
== 0))
331 } else if (!(pt
->paste_flags
& PASTE_OBJECTS
))
334 if (NULL
== (dst
= sheet_object_dup (src
)))
337 if (pt
->paste_flags
& PASTE_TRANSPOSE
) {
341 range_transpose (&tmp
.cell_bound
, pt
->sheet
, &origin
);
343 range_translate (&tmp
.cell_bound
, pt
->sheet
, left
, top
);
344 sheet_object_set_anchor (dst
, &tmp
);
345 sheet_object_set_sheet (dst
, pt
->sheet
);
346 g_object_unref (dst
);
350 cb_paste_cell (GnmCellCopy
const *src
, gconstpointer ignore
,
351 struct paste_cell_data
*dat
)
353 int target_col
= dat
->top_left
.col
;
354 int target_row
= dat
->top_left
.row
;
356 if (dat
->pt
->paste_flags
& PASTE_TRANSPOSE
) {
357 target_col
+= src
->offset
.row
;
358 target_row
+= src
->offset
.col
;
359 } else if (dat
->pt
->paste_flags
& PASTE_FLIP_H
) {
360 target_col
+= dat
->cr
->cols
- src
->offset
.col
- 1;
361 target_row
+= src
->offset
.row
;
362 } else if (dat
->pt
->paste_flags
& PASTE_FLIP_V
) {
363 target_col
+= src
->offset
.col
;
364 target_row
+= dat
->cr
->rows
- src
->offset
.row
- 1;
366 target_col
+= src
->offset
.col
;
367 target_row
+= src
->offset
.row
;
370 dat
->rinfo
.pos
.sheet
= dat
->pt
->sheet
;
371 if (dat
->pt
->paste_flags
& PASTE_EXPR_LOCAL_RELOCATE
) {
372 dat
->rinfo
.pos
.eval
.col
= dat
->cr
->base
.col
+ src
->offset
.col
;
373 dat
->rinfo
.pos
.eval
.row
= dat
->cr
->base
.row
+ src
->offset
.row
;
375 dat
->rinfo
.pos
.eval
.col
= target_col
;
376 dat
->rinfo
.pos
.eval
.row
= target_row
;
379 paste_cell (target_col
, target_row
, src
, dat
);
383 range_flip_h (GnmRange
*range
, Sheet
const *sheet
, int const *data
)
387 g_return_val_if_fail (range
!= NULL
, TRUE
);
389 t
= *data
- range
->end
.col
;
390 range
->end
.col
= *data
- range
->start
.col
;
391 range
->start
.col
= t
;
396 range_flip_v (GnmRange
*range
, Sheet
const *sheet
, int const *data
)
400 g_return_val_if_fail (range
!= NULL
, TRUE
);
402 t
= *data
- range
->end
.row
;
403 range
->end
.row
= *data
- range
->start
.row
;
404 range
->start
.row
= t
;
410 * clipboard_paste_region:
411 * @cr: The GnmCellRegion to paste.
412 * @pt: Where to paste the values.
413 * @cc: (nullable): The context for error handling.
415 * Pastes the supplied GnmCellRegion (@cr) into the supplied
416 * GnmPasteTarget (@pt). This operation is not undoable. It does not auto grow
417 * the destination if the target is a singleton. This is a simple interface to
420 * Returns: %TRUE if there was a problem.
423 clipboard_paste_region (GnmCellRegion
const *cr
,
424 GnmPasteTarget
const *pt
,
427 int repeat_horizontal
, repeat_vertical
, clearFlags
;
428 int dst_cols
, dst_rows
, src_cols
, src_rows
;
432 gboolean has_contents
, adjust_merges
= TRUE
;
433 struct paste_cell_data dat
;
434 GnmRange
const *merge_src
;
435 gboolean no_flipping
, do_col_widths
, do_row_heights
;
437 g_return_val_if_fail (pt
!= NULL
, TRUE
);
438 g_return_val_if_fail (cr
!= NULL
, TRUE
);
440 /* we do not need any of this fancy stuff when pasting a simple object */
441 if (cr
->cell_content
== NULL
&&
442 cr
->styles
== NULL
&&
443 cr
->merged
== NULL
&&
444 cr
->objects
!= NULL
) {
445 if (pt
->paste_flags
& (PASTE_COMMENTS
| PASTE_OBJECTS
))
446 for (ptr
= cr
->objects
; ptr
; ptr
= ptr
->next
)
447 paste_object (pt
, ptr
->data
,
448 pt
->range
.start
.col
, pt
->range
.start
.row
);
453 dst_cols
= range_width (r
);
454 dst_rows
= range_height (r
);
458 /* If the source is a single cell or a single merge */
459 /* Treat a target of a single merge specially, don't split the merge */
460 if ((src_cols
== 1 && src_rows
== 1) ||
461 (g_slist_length (cr
->merged
) == 1 &&
462 (NULL
!= (merge_src
= cr
->merged
->data
)) &&
463 range_height (merge_src
) == cr
->rows
&&
464 range_width (merge_src
) == cr
->cols
)) {
465 GnmRange
const *merge
= gnm_sheet_merge_is_corner (pt
->sheet
, &r
->start
);
466 if (merge
!= NULL
&& range_equal (r
, merge
)) {
467 dst_cols
= dst_rows
= 1;
468 adjust_merges
= FALSE
;
472 /* Apparently links do not supercede merges */
473 } else if (pt
->paste_flags
& PASTE_LINK
)
474 adjust_merges
= FALSE
;
476 has_contents
= pt
->paste_flags
& (PASTE_CONTENTS
|PASTE_AS_VALUES
|PASTE_LINK
);
478 if (pt
->paste_flags
& PASTE_TRANSPOSE
) {
484 if (cr
->not_as_contents
&& (pt
->paste_flags
& PASTE_CONTENTS
)) {
486 go_cmd_context_error_invalid
488 _("Unable to paste"),
489 _("Contents can only be pasted by value or by link."));
493 /* calculate the tiling */
494 repeat_horizontal
= dst_cols
/src_cols
;
495 if (repeat_horizontal
* src_cols
!= dst_cols
) {
496 char *msg
= g_strdup_printf (
497 _("destination does not have an even multiple of source columns (%d vs %d)\n\n"
498 "Try selecting a single cell or an area of the same shape and size."),
501 go_cmd_context_error_invalid (cc
, _("Unable to paste"), msg
);
506 repeat_vertical
= dst_rows
/src_rows
;
507 if (repeat_vertical
* src_rows
!= dst_rows
) {
508 char *msg
= g_strdup_printf (
509 _("destination does not have an even multiple of source rows (%d vs %d)\n\n"
510 "Try selecting a single cell or an area of the same shape and size."),
513 go_cmd_context_error_invalid (cc
, _("Unable to paste"), msg
);
518 if ((pt
->range
.start
.col
+ dst_cols
) > gnm_sheet_get_max_cols (pt
->sheet
) ||
519 (pt
->range
.start
.row
+ dst_rows
) > gnm_sheet_get_max_rows (pt
->sheet
)) {
521 go_cmd_context_error_invalid
523 _("Unable to paste"),
524 _("result passes the sheet boundary"));
529 /* clear the region where we will paste */
531 clearFlags
= CLEAR_VALUES
| CLEAR_NORESPAN
;
533 if (pt
->paste_flags
& PASTE_COMMENTS
)
534 clearFlags
|= CLEAR_COMMENTS
;
536 /* No need to clear the formats. We will paste over top of these. */
537 /* if (pt->paste_flags & PASTE_FORMATS) clearFlags |= CLEAR_FORMATS; */
539 if (pt
->paste_flags
& (PASTE_OPER_MASK
| PASTE_SKIP_BLANKS
))
542 /* remove merged regions even for operations, or blanks */
543 if (has_contents
&& adjust_merges
)
544 clearFlags
|= CLEAR_MERGES
;
546 if (clearFlags
!= 0) {
547 int const dst_col
= pt
->range
.start
.col
;
548 int const dst_row
= pt
->range
.start
.row
;
549 sheet_clear_region (pt
->sheet
,
551 dst_col
+ dst_cols
- 1,
552 dst_row
+ dst_rows
- 1,
556 dat
.translate_dates
= cr
->date_conv
&&
557 !go_date_conv_equal (cr
->date_conv
, sheet_date_conv (pt
->sheet
));
559 for (i
= 0; i
< repeat_horizontal
; i
++)
560 for (j
= 0; j
< repeat_vertical
; j
++) {
561 int const left
= i
* src_cols
+ pt
->range
.start
.col
;
562 int const top
= j
* src_rows
+ pt
->range
.start
.row
;
564 dat
.top_left
.col
= left
;
565 dat
.top_left
.row
= top
;
566 dat
.rinfo
.reloc_type
= GNM_EXPR_RELOCATE_MOVE_RANGE
;
567 dat
.rinfo
.origin_sheet
= dat
.rinfo
.target_sheet
= pt
->sheet
;
568 if (pt
->paste_flags
& PASTE_EXPR_LOCAL_RELOCATE
) {
569 dat
.rinfo
.origin
.start
= cr
->base
;
570 dat
.rinfo
.origin
.end
.col
= cr
->base
.col
+ cr
->cols
- 1;
571 dat
.rinfo
.origin
.end
.row
= cr
->base
.row
+ cr
->rows
- 1;
572 dat
.rinfo
.col_offset
= left
- cr
->base
.col
;
573 dat
.rinfo
.row_offset
= top
- cr
->base
.row
;
575 dat
.rinfo
.origin
= pt
->range
;
576 dat
.rinfo
.col_offset
= 0;
577 dat
.rinfo
.row_offset
= 0;
580 /* Move the styles on here so we get correct formats before recalc */
581 if (pt
->paste_flags
& PASTE_FORMATS
) {
582 if (pt
->paste_flags
& PASTE_TRANSPOSE
)
583 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
585 (sheet_style_set_list_cb_t
)
588 else if (pt
->paste_flags
& PASTE_FLIP_H
) {
589 int data
= 2 * left
+ src_cols
- 1;
590 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
592 (sheet_style_set_list_cb_t
)
593 range_flip_h
, &data
);
594 } else if (pt
->paste_flags
& PASTE_FLIP_V
) {
595 int data
= 2 * top
+ src_rows
- 1;
596 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
598 (sheet_style_set_list_cb_t
)
599 range_flip_v
, &data
);
601 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
602 cr
->styles
, NULL
, NULL
);
604 if (has_contents
&& !(pt
->paste_flags
& PASTE_DONT_MERGE
)) {
605 for (ptr
= cr
->merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
606 GnmRange tmp
= *((GnmRange
const *)ptr
->data
);
607 if (pt
->paste_flags
& PASTE_TRANSPOSE
) {
609 x
= tmp
.start
.col
; tmp
.start
.col
= tmp
.start
.row
; tmp
.start
.row
= x
;
610 x
= tmp
.end
.col
; tmp
.end
.col
= tmp
.end
.row
; tmp
.end
.row
= x
;
612 if (!range_translate (&tmp
, pt
->sheet
, left
, top
))
613 gnm_sheet_merge_add (pt
->sheet
, &tmp
, TRUE
, cc
);
617 if (has_contents
&& (pt
->paste_flags
& PASTE_LINK
)) {
618 paste_link (pt
, top
, left
, cr
);
622 if (has_contents
&& NULL
!= cr
->cell_content
) {
625 g_hash_table_foreach (cr
->cell_content
,
626 (GHFunc
)cb_paste_cell
, &dat
);
629 if (pt
->paste_flags
& (PASTE_COMMENTS
| PASTE_OBJECTS
))
630 for (ptr
= cr
->objects
; ptr
; ptr
= ptr
->next
)
631 paste_object (pt
, ptr
->data
, left
, top
);
634 no_flipping
= (pt
->paste_flags
& (PASTE_FLIP_H
| PASTE_FLIP_V
| PASTE_TRANSPOSE
)) == 0;
637 ((pt
->paste_flags
& PASTE_COLUMN_WIDTHS
) ||
638 ((pt
->paste_flags
& PASTE_COLUMN_WIDTHS_AUTO
) &&
640 src_rows
== gnm_sheet_get_max_rows (cr
->origin_sheet
)));
643 for (i
= 0; i
< repeat_horizontal
; i
++) {
644 int first
= pt
->range
.start
.col
+ i
* src_cols
;
645 colrow_set_states (pt
->sheet
, TRUE
, first
, cr
->col_state
);
651 ((pt
->paste_flags
& PASTE_ROW_HEIGHTS
) ||
652 ((pt
->paste_flags
& PASTE_ROW_HEIGHTS_AUTO
) &&
654 src_cols
== gnm_sheet_get_max_cols (cr
->origin_sheet
)));
655 if (do_row_heights
) {
657 for (i
= 0; i
< repeat_vertical
; i
++) {
658 int first
= pt
->range
.start
.row
+ i
* src_rows
;
659 colrow_set_states (pt
->sheet
, FALSE
, first
, cr
->row_state
);
663 if (!(pt
->paste_flags
& PASTE_NO_RECALC
)) {
665 sheet_region_queue_recalc (pt
->sheet
, r
);
666 sheet_flag_status_update_range (pt
->sheet
, r
);
668 sheet_flag_style_update_range (pt
->sheet
, r
);
670 sheet_range_calc_spans (pt
->sheet
, r
,
671 (pt
->paste_flags
& PASTE_FORMATS
) ? GNM_SPANCALC_RE_RENDER
: GNM_SPANCALC_RENDER
);
672 sheet_redraw_all (pt
->sheet
, FALSE
);
679 cb_clipboard_prepend_cell (GnmCellIter
const *iter
, GnmCellRegion
*cr
)
682 GnmCellCopy
*copy
= gnm_cell_copy_new (cr
,
683 iter
->pp
.eval
.col
- cr
->base
.col
,
684 iter
->pp
.eval
.row
- cr
->base
.row
);
685 copy
->val
= value_dup (iter
->cell
->value
);
687 if (gnm_cell_has_expr (iter
->cell
)) {
688 gnm_expr_top_ref (copy
->texpr
= iter
->cell
->base
.texpr
);
690 /* Check for array division */
691 if (!cr
->not_as_contents
&&
692 gnm_cell_array_bound (iter
->cell
, &a
) &&
693 (a
.start
.col
< cr
->base
.col
||
694 a
.start
.row
< cr
->base
.row
||
695 a
.end
.col
>= (cr
->base
.col
+ cr
->cols
) ||
696 a
.end
.row
>= (cr
->base
.row
+ cr
->rows
)))
697 cr
->not_as_contents
= TRUE
;
705 cb_dup_objects (SheetObject
const *src
, GnmCellRegion
*cr
)
707 SheetObject
*dst
= sheet_object_dup (src
);
709 SheetObjectAnchor tmp
= *sheet_object_get_anchor (src
);
710 range_translate (&tmp
.cell_bound
, sheet_object_get_sheet (src
),
711 - cr
->base
.col
, - cr
->base
.row
);
712 sheet_object_set_anchor (dst
, &tmp
);
713 cr
->objects
= g_slist_prepend (cr
->objects
, dst
);
718 * clipboard_copy_range:
720 * Entry point to the clipboard copy code
723 clipboard_copy_range (Sheet
*sheet
, GnmRange
const *r
)
726 GSList
*merged
, *ptr
;
729 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
730 g_return_val_if_fail (range_is_sane (r
), NULL
);
732 cr
= gnm_cell_region_new (sheet
);
734 cr
->cols
= range_width (r
);
735 cr
->rows
= range_height (r
);
736 cr
->col_state
= colrow_get_states (sheet
,
737 TRUE
, r
->start
.col
, r
->end
.col
);
738 cr
->row_state
= colrow_get_states (sheet
,
739 FALSE
, r
->start
.row
, r
->end
.row
);
741 sheet_foreach_cell_in_range ( sheet
, CELL_ITER_IGNORE_NONEXISTENT
, r
,
742 (CellIterFunc
) cb_clipboard_prepend_cell
,
744 objects
= sheet_objects_get (sheet
, r
, G_TYPE_NONE
);
745 g_slist_foreach (objects
, (GFunc
)cb_dup_objects
, cr
);
746 g_slist_free (objects
);
748 cr
->styles
= sheet_style_get_range (sheet
, r
);
750 merged
= gnm_sheet_merge_get_overlap (sheet
, r
);
751 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
752 GnmRange
*tmp
= gnm_range_dup (ptr
->data
);
753 range_translate (tmp
, sheet
, -r
->start
.col
, -r
->start
.row
);
754 cr
->merged
= g_slist_prepend (cr
->merged
, tmp
);
756 g_slist_free (merged
);
762 cb_clipboard_copy_range_undo (GnmCellRegion
*cr
, GnmSheetRange
*sr
,
767 clipboard_paste_region
769 paste_target_init (&pt
,
772 PASTE_CONTENTS
| PASTE_FORMATS
|
773 PASTE_OBJECTS
| PASTE_COMMENTS
|
774 PASTE_COLUMN_WIDTHS
| PASTE_ROW_HEIGHTS
),
779 * clipboard_copy_range_undo:
783 * Returns: (transfer full): A #GOUndo object that will restore the contents
784 * of the given range.
787 clipboard_copy_range_undo (Sheet
*sheet
, GnmRange
const *r
)
789 GnmCellRegion
*cr
= clipboard_copy_range (sheet
, r
);
790 g_return_val_if_fail (cr
!= NULL
, NULL
);
791 return go_undo_binary_new (cr
, gnm_sheet_range_new (sheet
, r
),
792 (GOUndoBinaryFunc
)cb_clipboard_copy_range_undo
,
793 (GFreeFunc
)cellregion_unref
,
798 * clipboard_copy_ranges_undo:
800 * @ranges: (element-type GnmRange) (transfer none): list of ranges
802 * Returns: (transfer full): A #GOUndo object that will restore the contents
803 * of the given range.
806 clipboard_copy_ranges_undo (Sheet
*sheet
, GSList
*ranges
)
811 for (l
= ranges
; l
!= NULL
; l
= l
->next
) {
812 GnmRange
*r
= l
->data
;
813 GOUndo
*undo1
= clipboard_copy_range_undo (sheet
, r
);
814 undo
= go_undo_combine (undo
, undo1
);
822 * clipboard_copy_obj:
824 * @objects: (element-type SheetObject): #GSList
826 * Returns a cell region with copies of objects in list. Caller is responsible
827 * for cellregion_unref-ing the result.
830 clipboard_copy_obj (Sheet
*sheet
, GSList
*objects
)
832 SheetObjectAnchor tmp_anchor
;
833 SheetObjectAnchor
const *anchor
;
841 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
842 g_return_val_if_fail (objects
!= NULL
, NULL
);
844 cr
= gnm_cell_region_new (sheet
);
845 for (ptr
= objects
; ptr
!= NULL
; ptr
= ptr
->next
)
846 if (NULL
!= (so
= sheet_object_dup (ptr
->data
))) {
847 anchor
= sheet_object_get_anchor (so
);
849 #warning FIXME : This is only used in gnm_sog_write_image
850 /* NOTE #1 : It seems necessary to handle pasting an object that has been removed from
851 * the sheet after being added to the clipboard. it seems like we would need
852 * this sort of information for anything that implements SheetObjectImageableIface
854 sheet_object_anchor_to_pts (anchor
, sheet
, coords
);
855 w
= fabs (coords
[2] - coords
[0]) + 1.5;
856 h
= fabs (coords
[3] - coords
[1]) + 1.5;
857 g_object_set_data (G_OBJECT (so
), "pt-width-at-copy",
858 GUINT_TO_POINTER (w
));
859 g_object_set_data (G_OBJECT (so
), "pt-height-at-copy",
860 GUINT_TO_POINTER (h
));
862 tmp_anchor
= *anchor
;
863 r
= &tmp_anchor
.cell_bound
;
864 range_translate (r
, sheet
,
865 -MIN (r
->start
.col
, r
->end
.col
),
866 -MIN (r
->start
.row
, r
->end
.row
));
867 sheet_object_set_anchor (so
, &tmp_anchor
);
869 cr
->objects
= g_slist_prepend (cr
->objects
, so
);
876 paste_target_init (GnmPasteTarget
*pt
, Sheet
*sheet
,
877 GnmRange
const *r
, GnmPasteFlags flags
)
879 pt
->sheet
= sheet
; // No ref
881 pt
->paste_flags
= flags
;
886 * gnm_cell_region_new:
887 * @origin_sheet: optionally NULL.
889 * A convenience routine to create CellRegions and init the flags nicely.
892 gnm_cell_region_new (Sheet
*origin_sheet
)
894 GnmCellRegion
*cr
= g_new0 (GnmCellRegion
, 1);
895 cr
->origin_sheet
= origin_sheet
;
896 cr
->date_conv
= origin_sheet
&& origin_sheet
->workbook
897 ? sheet_date_conv (origin_sheet
)
899 cr
->cols
= cr
->rows
= -1;
900 cr
->not_as_contents
= FALSE
;
901 cr
->cell_content
= NULL
;
902 cr
->col_state
= NULL
;
903 cr
->row_state
= NULL
;
913 cellregion_ref (GnmCellRegion
*cr
)
915 g_return_val_if_fail (cr
!= NULL
, NULL
);
921 cellregion_unref (GnmCellRegion
*cr
)
923 g_return_if_fail (cr
!= NULL
);
925 if (cr
->ref_count
> 1) {
930 if (NULL
!= cr
->cell_content
) {
931 g_hash_table_destroy (cr
->cell_content
);
932 cr
->cell_content
= NULL
;
935 if (NULL
!= cr
->col_state
)
936 cr
->col_state
= colrow_state_list_destroy (cr
->col_state
);
937 if (NULL
!= cr
->row_state
)
938 cr
->row_state
= colrow_state_list_destroy (cr
->row_state
);
939 if (cr
->styles
!= NULL
) {
940 style_list_free (cr
->styles
);
943 if (cr
->merged
!= NULL
) {
945 for (ptr
= cr
->merged
; ptr
!= NULL
; ptr
= ptr
->next
)
947 g_slist_free (cr
->merged
);
950 if (cr
->objects
!= NULL
) {
952 for (ptr
= cr
->objects
; ptr
!= NULL
; ptr
= ptr
->next
)
953 g_object_unref (ptr
->data
);
954 g_slist_free (cr
->objects
);
962 gnm_cell_region_get_type (void)
967 t
= g_boxed_type_register_static ("GnmCellRegion",
968 (GBoxedCopyFunc
)cellregion_ref
,
969 (GBoxedFreeFunc
)cellregion_unref
);
975 cellregion_get_content (GnmCellRegion
const *cr
, int col
, int row
)
977 if (cr
->cell_content
) {
981 return g_hash_table_lookup (cr
->cell_content
, &pos
);
987 cb_invalidate_cellcopy (GnmCellCopy
*cc
, gconstpointer ignore
,
988 GnmExprRelocateInfo
*rinfo
)
990 GnmExprTop
const *texpr
;
991 if (NULL
!= cc
->texpr
) {
992 texpr
= gnm_expr_top_relocate (cc
->texpr
, rinfo
, FALSE
);
994 gnm_expr_top_unref (cc
->texpr
);
1001 * cellregion_invalidate_sheet:
1002 * @cr: #GnmCellRegion
1005 * Invalidate references from cell content, objects or style to @sheet.
1008 cellregion_invalidate_sheet (GnmCellRegion
*cr
,
1012 gboolean save_invalidated
;
1013 GnmExprRelocateInfo rinfo
;
1015 g_return_if_fail (cr
!= NULL
);
1016 g_return_if_fail (IS_SHEET (sheet
));
1018 save_invalidated
= sheet
->being_invalidated
;
1019 sheet
->being_invalidated
= TRUE
;
1021 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_INVALIDATE_SHEET
;
1022 if (NULL
!= cr
->cell_content
)
1023 g_hash_table_foreach (cr
->cell_content
,
1024 (GHFunc
)cb_invalidate_cellcopy
, &rinfo
);
1025 sheet
->being_invalidated
= save_invalidated
;
1027 for (ptr
= cr
->objects
; ptr
!= NULL
; ptr
= ptr
->next
)
1028 sheet_object_invalidate_sheet (ptr
->data
, sheet
);
1030 if (cr
->origin_sheet
== sheet
)
1031 cr
->origin_sheet
= NULL
;
1035 cb_cellregion_extent (GnmCellCopy
*cc
, gconstpointer ignore
, GnmRange
*extent
)
1037 if (extent
->start
.col
>= 0) {
1038 if (extent
->start
.col
> cc
->offset
.col
)
1039 extent
->start
.col
= cc
->offset
.col
;
1040 else if (extent
->end
.col
< cc
->offset
.col
)
1041 extent
->end
.col
= cc
->offset
.col
;
1043 if (extent
->start
.row
> cc
->offset
.row
)
1044 extent
->start
.row
= cc
->offset
.row
;
1045 else if (extent
->end
.row
< cc
->offset
.row
)
1046 extent
->end
.row
= cc
->offset
.row
;
1047 } else /* first cell */
1048 extent
->start
= extent
->end
= cc
->offset
;
1052 * cellregion_extent:
1053 * @cr: #GnmCellRegion
1054 * @extent: #GnmRange
1056 * Find the min and max col/row with cell content
1059 cellregion_extent (GnmCellRegion
const *cr
, GnmRange
*extent
)
1061 if (NULL
!= cr
->cell_content
) {
1062 range_init (extent
, -1, -1, -1, -1);
1063 g_hash_table_foreach (cr
->cell_content
,
1064 (GHFunc
)cb_cellregion_extent
, extent
);
1066 range_init (extent
, 0, 0, 0, 0);
1070 cellregion_to_string (GnmCellRegion
const *cr
,
1071 gboolean only_visible
,
1072 GODateConventions
const *date_conv
)
1074 GString
*all
, *line
;
1075 GnmCellCopy
const *cc
;
1076 int col
, row
, next_col_check
, next_row_check
;
1078 ColRowStateList
const *col_state
= NULL
, *row_state
= NULL
;
1079 ColRowRLEState
const *rle
;
1081 GnmStyle
const *style
;
1082 GOFormat
const *fmt
;
1084 g_return_val_if_fail (cr
!= NULL
, NULL
);
1085 g_return_val_if_fail (cr
->rows
>= 0, NULL
);
1086 g_return_val_if_fail (cr
->cols
>= 0, NULL
);
1088 /* pre-allocate rough approximation of buffer */
1089 ncells
= cr
->cell_content
? g_hash_table_size (cr
->cell_content
) : 0;
1090 all
= g_string_sized_new (20 * ncells
+ 1);
1091 line
= g_string_new (NULL
);
1093 cellregion_extent (cr
, &extent
);
1095 if (only_visible
&& NULL
!= (row_state
= cr
->row_state
)) {
1096 next_row_check
= i
= 0;
1097 while ((i
+= ((ColRowRLEState
*)(row_state
->data
))->length
) <= extent
.start
.row
) {
1098 if (NULL
== (row_state
= row_state
->next
)) {
1099 next_row_check
= gnm_sheet_get_max_rows (cr
->origin_sheet
);
1105 next_row_check
= gnm_sheet_get_max_rows (cr
->origin_sheet
);
1107 for (row
= extent
.start
.row
; row
<= extent
.end
.row
;) {
1108 if (row
>= next_row_check
) {
1109 rle
= row_state
->data
;
1110 row_state
= row_state
->next
;
1111 next_row_check
+= rle
->length
;
1112 if (!rle
->state
.visible
) {
1113 row
= next_row_check
;
1118 g_string_assign (line
, "");
1120 if (only_visible
&& NULL
!= (col_state
= cr
->col_state
)) {
1121 next_col_check
= i
= 0;
1122 while ((i
+= ((ColRowRLEState
*)(col_state
->data
))->length
) <= extent
.start
.col
) {
1123 if (NULL
== (col_state
= col_state
->next
)) {
1124 next_col_check
= gnm_sheet_get_max_cols (cr
->origin_sheet
);
1130 next_col_check
= gnm_sheet_get_max_cols (cr
->origin_sheet
);
1132 for (col
= extent
.start
.col
; col
<= extent
.end
.col
;) {
1133 if (col
== next_col_check
) {
1134 rle
= col_state
->data
;
1135 col_state
= col_state
->next
;
1136 next_col_check
+= rle
->length
;
1137 if (!rle
->state
.visible
) {
1138 col
= next_col_check
;
1143 cc
= cellregion_get_content (cr
, col
, row
);
1145 style
= style_list_get_style (cr
->styles
, col
, row
);
1146 fmt
= gnm_style_get_format (style
);
1148 if (go_format_is_general (fmt
) &&
1149 VALUE_FMT (cc
->val
))
1150 fmt
= VALUE_FMT (cc
->val
);
1152 format_value_gstring (line
, fmt
, cc
->val
,
1155 if (++col
<= extent
.end
.col
)
1156 g_string_append_c (line
, '\t');
1158 g_string_append_len (all
, line
->str
, line
->len
);
1159 if (++row
<= extent
.end
.row
)
1160 g_string_append_c (all
, '\n');
1163 g_string_free (line
, TRUE
);
1168 cellregion_cmd_size (GnmCellRegion
const *cr
)
1172 g_return_val_if_fail (cr
!= NULL
, 1);
1174 res
+= g_slist_length (cr
->styles
);
1175 if (NULL
!= cr
->cell_content
)
1176 res
+= g_hash_table_size (cr
->cell_content
);
1181 gnm_cell_copy_free (GnmCellCopy
*cc
)
1184 gnm_expr_top_unref (cc
->texpr
);
1187 value_release (cc
->val
);
1190 CHUNK_FREE (cell_copy_pool
, cc
);
1194 gnm_cell_copy_new (GnmCellRegion
*cr
, int col_offset
, int row_offset
)
1196 GnmCellCopy
*res
= CHUNK_ALLOC (GnmCellCopy
, cell_copy_pool
);
1197 ((GnmCellPos
*)(&res
->offset
))->col
= col_offset
;
1198 ((GnmCellPos
*)(&res
->offset
))->row
= row_offset
;
1202 if (NULL
== cr
->cell_content
)
1203 cr
->cell_content
= g_hash_table_new_full (
1204 (GHashFunc
)&gnm_cellpos_hash
,
1205 (GCompareFunc
)&gnm_cellpos_equal
,
1206 (GDestroyNotify
) gnm_cell_copy_free
,
1209 g_hash_table_insert (cr
->cell_content
, res
, res
);
1215 * clipboard_init: (skip)
1218 clipboard_init (void)
1220 #if USE_CELL_COPY_POOLS
1222 go_mem_chunk_new ("cell copy pool",
1223 sizeof (GnmCellCopy
),
1228 #if USE_CELL_COPY_POOLS
1230 cb_cell_copy_pool_leak (gpointer data
, G_GNUC_UNUSED gpointer user
)
1232 GnmCellCopy
const *cc
= data
;
1233 g_printerr ("Leaking cell copy at %p.\n", (void *)cc
);
1238 * clipboard_shutdown: (skip)
1241 clipboard_shutdown (void)
1243 #if USE_CELL_COPY_POOLS
1244 go_mem_chunk_foreach_leak (cell_copy_pool
, cb_cell_copy_pool_leak
, NULL
);
1245 go_mem_chunk_destroy (cell_copy_pool
, FALSE
);
1246 cell_copy_pool
= NULL
;