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 GnmPasteTarget
*res
= g_new (GnmPasteTarget
, 1);
90 memcpy (res
, pt
, sizeof (GnmPasteTarget
));
95 gnm_paste_target_get_type (void)
100 t
= g_boxed_type_register_static ("GnmPasteTarget",
101 (GBoxedCopyFunc
)gnm_paste_target_copy
,
102 (GBoxedFreeFunc
)g_free
);
108 cell_has_expr_or_number_or_blank (GnmCell
const * cell
)
110 return (gnm_cell_is_empty (cell
) ||
111 (cell
!= NULL
&& gnm_cell_is_number (cell
)) ||
112 (cell
!= NULL
&& gnm_cell_has_expr (cell
)));
115 static GnmExpr
const *
116 contents_as_expr (GnmExprTop
const *texpr
, GnmValue
const *val
)
119 return gnm_expr_copy (texpr
->expr
);
120 if (VALUE_IS_EMPTY (val
))
121 return gnm_expr_new_constant (value_new_float (0.0));
122 if (VALUE_IS_NUMBER (val
))
123 return gnm_expr_new_constant (value_dup (val
));
128 paste_op_to_expr_op (int paste_flags
)
130 g_return_val_if_fail (paste_flags
& PASTE_OPER_MASK
, 0);
132 if (paste_flags
& PASTE_OPER_ADD
)
133 return GNM_EXPR_OP_ADD
;
134 else if (paste_flags
& PASTE_OPER_SUB
)
135 return GNM_EXPR_OP_SUB
;
136 else if (paste_flags
& PASTE_OPER_MULT
)
137 return GNM_EXPR_OP_MULT
;
138 else if (paste_flags
& PASTE_OPER_DIV
)
139 return GNM_EXPR_OP_DIV
;
145 paste_cell_with_operation (Sheet
*dst_sheet
,
146 int target_col
, int target_row
,
147 GnmExprRelocateInfo
const *rinfo
,
148 GnmCellCopy
const *src
,
154 if (src
->texpr
== NULL
&&
155 !VALUE_IS_EMPTY (src
->val
) &&
156 !VALUE_IS_NUMBER (src
->val
))
159 dst
= sheet_cell_fetch (dst_sheet
, target_col
, target_row
);
160 if (!cell_has_expr_or_number_or_blank (dst
))
163 op
= paste_op_to_expr_op (paste_flags
);
164 /* FIXME : This does not handle arrays, linked cells, ranges, etc. */
165 if ((paste_flags
& PASTE_CONTENTS
) &&
166 (NULL
!= src
->texpr
|| gnm_cell_has_expr (dst
))) {
167 GnmExpr
const *old_expr
= contents_as_expr (dst
->base
.texpr
, dst
->value
);
168 GnmExpr
const *copied_expr
= contents_as_expr (src
->texpr
, src
->val
);
169 GnmExprTop
const *res
= gnm_expr_top_new (gnm_expr_new_binary (old_expr
, op
, copied_expr
));
170 GnmExprTop
const *relo
= gnm_expr_top_relocate (res
, rinfo
, FALSE
);
172 gnm_cell_set_expr (dst
, relo
);
173 gnm_expr_top_unref (relo
);
175 gnm_cell_set_expr (dst
, res
);
176 gnm_expr_top_unref (res
);
180 GnmExpr
const *expr
= gnm_expr_new_binary (
181 gnm_expr_new_constant (value_dup (dst
->value
)),
183 gnm_expr_new_constant (value_dup (src
->val
)));
184 GnmExprTop
const *texpr
= gnm_expr_top_new (expr
);
186 eval_pos_init_cell (&pos
, dst
);
187 pos
.dep
= NULL
; /* no dynamic deps */
188 value
= gnm_expr_top_eval (texpr
, &pos
,
189 GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
190 gnm_expr_top_unref (texpr
);
191 gnm_cell_set_value (dst
, value
);
195 /* NOTE : Make sure to set up any merged regions in the target range BEFORE
199 paste_link (GnmPasteTarget
const *pt
, int top
, int left
,
200 GnmCellRegion
const *cr
)
203 GnmCellRef source_cell_ref
;
206 /* Not possible to link to arbitrary (non gnumeric) sources yet. */
207 /* TODO : eventually support interprocess gnumeric links */
208 if (cr
->origin_sheet
== NULL
)
211 /* TODO : support relative links ? */
212 source_cell_ref
.col_relative
= 0;
213 source_cell_ref
.row_relative
= 0;
214 source_cell_ref
.sheet
= (cr
->origin_sheet
!= pt
->sheet
)
215 ? cr
->origin_sheet
: NULL
;
217 for (x
= 0 ; x
< cr
->cols
; x
++, pos
.col
++) {
218 source_cell_ref
.col
= cr
->base
.col
+ x
;
220 for (y
= 0 ; y
< cr
->rows
; y
++, pos
.row
++) {
221 GnmExprTop
const *texpr
;
223 sheet_cell_fetch (pt
->sheet
, pos
.col
, pos
.row
);
225 /* This could easily be made smarter */
226 if (!gnm_cell_is_merged (cell
) &&
227 gnm_sheet_merge_contains_pos (pt
->sheet
, &pos
))
229 source_cell_ref
.row
= cr
->base
.row
+ y
;
230 texpr
= gnm_expr_top_new (gnm_expr_new_cellref (&source_cell_ref
));
231 gnm_cell_set_expr (cell
, texpr
);
232 gnm_expr_top_unref (texpr
);
237 struct paste_cell_data
{
238 GnmPasteTarget
const *pt
;
239 GnmCellRegion
const *cr
;
241 GnmExprRelocateInfo rinfo
;
242 gboolean translate_dates
;
247 * @target_col: Column to put the cell into
248 * @target_row: Row to put the cell into.
249 * @src: A #GnmCelCopy with the content to paste
250 * @paste_flags: Bit mask that describes the paste options.
252 * Pastes a cell in the spreadsheet.
255 paste_cell (int target_col
, int target_row
,
256 GnmCellCopy
const *src
,
257 const struct paste_cell_data
*dat
)
259 Sheet
*dst_sheet
= dat
->pt
->sheet
;
260 int paste_flags
= dat
->pt
->paste_flags
;
262 if (paste_flags
& PASTE_OPER_MASK
)
263 paste_cell_with_operation (dst_sheet
, target_col
, target_row
,
264 &dat
->rinfo
, src
, paste_flags
);
266 GnmCell
*dst
= sheet_cell_fetch (dst_sheet
, target_col
, target_row
);
267 if (NULL
!= src
->texpr
&& (paste_flags
& PASTE_CONTENTS
)) {
268 GnmExprTop
const *relo
= gnm_expr_top_relocate (
269 src
->texpr
, &dat
->rinfo
, FALSE
);
270 if (paste_flags
& PASTE_TRANSPOSE
) {
271 GnmExprTop
const *trelo
=
272 gnm_expr_top_transpose (relo
? relo
: src
->texpr
);
275 gnm_expr_top_unref (relo
);
278 } else if (!relo
&& gnm_expr_top_is_array_corner (src
->texpr
)) {
279 /* We must not share array expressions. */
280 relo
= gnm_expr_top_new (gnm_expr_copy (src
->texpr
->expr
));
282 gnm_cell_set_expr_and_value (dst
, relo
? relo
: src
->texpr
,
283 value_dup (src
->val
), TRUE
);
285 gnm_expr_top_unref (relo
);
287 GnmValue
*newval
= NULL
;
288 GnmValue
const *oldval
= src
->val
;
290 if (dat
->translate_dates
&& oldval
&& VALUE_IS_FLOAT (oldval
)) {
291 GOFormat
const *fmt
= VALUE_FMT (oldval
)
293 : gnm_style_get_format (gnm_cell_get_style (dst
));
294 if (go_format_is_date (fmt
) > 0) {
295 gnm_float fnew
= go_date_conv_translate
296 (value_get_as_float (oldval
),
298 workbook_date_conv (dst_sheet
->workbook
));
299 newval
= value_new_float (fnew
);
300 value_set_fmt (newval
, VALUE_FMT (oldval
));
305 newval
= value_dup (src
->val
);
306 gnm_cell_set_value (dst
, newval
);
312 paste_object (GnmPasteTarget
const *pt
, SheetObject
const *src
, int left
, int top
)
315 SheetObjectAnchor tmp
;
317 tmp
= *sheet_object_get_anchor (src
);
318 if (G_OBJECT_TYPE (src
) == GNM_CELL_COMMENT_TYPE
) {
319 if ((pt
->paste_flags
& PASTE_COMMENTS
) &&
320 (pt
->paste_flags
& PASTE_IGNORE_COMMENTS_AT_ORIGIN
&&
321 tmp
.cell_bound
.start
.col
== 0 &&
322 tmp
.cell_bound
.start
.row
== 0))
324 } else if (!(pt
->paste_flags
& PASTE_OBJECTS
))
327 if (NULL
== (dst
= sheet_object_dup (src
)))
330 if (pt
->paste_flags
& PASTE_TRANSPOSE
) {
334 range_transpose (&tmp
.cell_bound
, pt
->sheet
, &origin
);
336 range_translate (&tmp
.cell_bound
, pt
->sheet
, left
, top
);
337 sheet_object_set_anchor (dst
, &tmp
);
338 sheet_object_set_sheet (dst
, pt
->sheet
);
339 g_object_unref (dst
);
343 cb_paste_cell (GnmCellCopy
const *src
, gconstpointer ignore
,
344 struct paste_cell_data
*dat
)
346 int target_col
= dat
->top_left
.col
;
347 int target_row
= dat
->top_left
.row
;
349 if (dat
->pt
->paste_flags
& PASTE_TRANSPOSE
) {
350 target_col
+= src
->offset
.row
;
351 target_row
+= src
->offset
.col
;
352 } else if (dat
->pt
->paste_flags
& PASTE_FLIP_H
) {
353 target_col
+= dat
->cr
->cols
- src
->offset
.col
- 1;
354 target_row
+= src
->offset
.row
;
355 } else if (dat
->pt
->paste_flags
& PASTE_FLIP_V
) {
356 target_col
+= src
->offset
.col
;
357 target_row
+= dat
->cr
->rows
- src
->offset
.row
- 1;
359 target_col
+= src
->offset
.col
;
360 target_row
+= src
->offset
.row
;
363 dat
->rinfo
.pos
.sheet
= dat
->pt
->sheet
;
364 if (dat
->pt
->paste_flags
& PASTE_EXPR_LOCAL_RELOCATE
) {
365 dat
->rinfo
.pos
.eval
.col
= dat
->cr
->base
.col
+ src
->offset
.col
;
366 dat
->rinfo
.pos
.eval
.row
= dat
->cr
->base
.row
+ src
->offset
.row
;
368 dat
->rinfo
.pos
.eval
.col
= target_col
;
369 dat
->rinfo
.pos
.eval
.row
= target_row
;
372 paste_cell (target_col
, target_row
, src
, dat
);
376 range_flip_h (GnmRange
*range
, Sheet
const *sheet
, int const *data
)
380 g_return_val_if_fail (range
!= NULL
, TRUE
);
382 t
= *data
- range
->end
.col
;
383 range
->end
.col
= *data
- range
->start
.col
;
384 range
->start
.col
= t
;
389 range_flip_v (GnmRange
*range
, Sheet
const *sheet
, int const *data
)
393 g_return_val_if_fail (range
!= NULL
, TRUE
);
395 t
= *data
- range
->end
.row
;
396 range
->end
.row
= *data
- range
->start
.row
;
397 range
->start
.row
= t
;
403 * clipboard_paste_region:
404 * @cr: The GnmCellRegion to paste.
405 * @pt: Where to paste the values.
406 * @cc: The context for error handling.
408 * Pastes the supplied GnmCellRegion (@cr) into the supplied
409 * GnmPasteTarget (@pt). This operation is not undoable. It does not auto grow
410 * the destination if the target is a singleton. This is a simple interface to
413 * returns : TRUE if there was a problem.
416 clipboard_paste_region (GnmCellRegion
const *cr
,
417 GnmPasteTarget
const *pt
,
420 int repeat_horizontal
, repeat_vertical
, clearFlags
;
421 int dst_cols
, dst_rows
, src_cols
, src_rows
;
425 gboolean has_contents
, adjust_merges
= TRUE
;
426 struct paste_cell_data dat
;
427 GnmRange
const *merge_src
;
429 g_return_val_if_fail (pt
!= NULL
, TRUE
);
430 g_return_val_if_fail (cr
!= NULL
, TRUE
);
432 /* we do not need any of this fancy stuff when pasting a simple object */
433 if (cr
->cell_content
== NULL
&&
434 cr
->styles
== NULL
&&
435 cr
->merged
== NULL
&&
436 cr
->objects
!= NULL
) {
437 if (pt
->paste_flags
& (PASTE_COMMENTS
| PASTE_OBJECTS
))
438 for (ptr
= cr
->objects
; ptr
; ptr
= ptr
->next
)
439 paste_object (pt
, ptr
->data
,
440 pt
->range
.start
.col
, pt
->range
.start
.row
);
445 dst_cols
= range_width (r
);
446 dst_rows
= range_height (r
);
452 /* If the source is a single cell or a single merge */
453 /* Treat a target of a single merge specially, don't split the merge */
454 if ((src_cols
== 1 && src_rows
== 1) ||
455 (g_slist_length (cr
->merged
) == 1 &&
456 (NULL
!= (merge_src
= cr
->merged
->data
)) &&
457 range_height (merge_src
) == cr
->rows
&&
458 range_width (merge_src
) == cr
->cols
)) {
459 GnmRange
const *merge
= gnm_sheet_merge_is_corner (pt
->sheet
, &r
->start
);
460 if (merge
!= NULL
&& range_equal (r
, merge
)) {
461 dst_cols
= dst_rows
= 1;
462 adjust_merges
= FALSE
;
466 /* Apparently links do not supercede merges */
467 } else if (pt
->paste_flags
& PASTE_LINK
)
468 adjust_merges
= FALSE
;
470 has_contents
= pt
->paste_flags
& (PASTE_CONTENTS
|PASTE_AS_VALUES
|PASTE_LINK
);
472 if (pt
->paste_flags
& PASTE_TRANSPOSE
) {
478 if (cr
->not_as_contents
&& (pt
->paste_flags
& PASTE_CONTENTS
)) {
479 go_cmd_context_error_invalid (cc
,
480 _("Unable to paste"),
481 _("Contents can only be pasted by value or by link."));
485 /* calculate the tiling */
486 repeat_horizontal
= dst_cols
/src_cols
;
487 if (repeat_horizontal
* src_cols
!= dst_cols
) {
488 char *msg
= g_strdup_printf (
489 _("destination does not have an even multiple of source columns (%d vs %d)\n\n"
490 "Try selecting a single cell or an area of the same shape and size."),
492 go_cmd_context_error_invalid (cc
, _("Unable to paste"), msg
);
497 repeat_vertical
= dst_rows
/src_rows
;
498 if (repeat_vertical
* src_rows
!= dst_rows
) {
499 char *msg
= g_strdup_printf (
500 _("destination does not have an even multiple of source rows (%d vs %d)\n\n"
501 "Try selecting a single cell or an area of the same shape and size."),
503 go_cmd_context_error_invalid (cc
, _("Unable to paste"), msg
);
508 if ((pt
->range
.start
.col
+ dst_cols
) > gnm_sheet_get_max_cols (pt
->sheet
) ||
509 (pt
->range
.start
.row
+ dst_rows
) > gnm_sheet_get_max_rows (pt
->sheet
)) {
510 go_cmd_context_error_invalid (cc
,
511 _("Unable to paste"),
512 _("result passes the sheet boundary"));
517 /* clear the region where we will paste */
519 clearFlags
= CLEAR_VALUES
| CLEAR_NORESPAN
;
521 if (pt
->paste_flags
& PASTE_COMMENTS
)
522 clearFlags
|= CLEAR_COMMENTS
;
524 /* No need to clear the formats. We will paste over top of these. */
525 /* if (pt->paste_flags & PASTE_FORMATS) clearFlags |= CLEAR_FORMATS; */
527 if (pt
->paste_flags
& (PASTE_OPER_MASK
| PASTE_SKIP_BLANKS
))
530 /* remove merged regions even for operations, or blanks */
531 if (has_contents
&& adjust_merges
)
532 clearFlags
|= CLEAR_MERGES
;
534 if (clearFlags
!= 0) {
535 int const dst_col
= pt
->range
.start
.col
;
536 int const dst_row
= pt
->range
.start
.row
;
537 sheet_clear_region (pt
->sheet
,
539 dst_col
+ dst_cols
- 1,
540 dst_row
+ dst_rows
- 1,
544 dat
.translate_dates
= cr
->date_conv
&&
545 !go_date_conv_equal (cr
->date_conv
, workbook_date_conv (pt
->sheet
->workbook
));
547 for (i
= 0; i
< repeat_horizontal
; i
++)
548 for (j
= 0; j
< repeat_vertical
; j
++) {
549 int const left
= i
* src_cols
+ pt
->range
.start
.col
;
550 int const top
= j
* src_rows
+ pt
->range
.start
.row
;
552 dat
.top_left
.col
= left
;
553 dat
.top_left
.row
= top
;
554 dat
.rinfo
.reloc_type
= GNM_EXPR_RELOCATE_MOVE_RANGE
;
555 dat
.rinfo
.origin_sheet
= dat
.rinfo
.target_sheet
= pt
->sheet
;
556 if (pt
->paste_flags
& PASTE_EXPR_LOCAL_RELOCATE
) {
557 dat
.rinfo
.origin
.start
= cr
->base
;
558 dat
.rinfo
.origin
.end
.col
= cr
->base
.col
+ cr
->cols
- 1;
559 dat
.rinfo
.origin
.end
.row
= cr
->base
.row
+ cr
->rows
- 1;
560 dat
.rinfo
.col_offset
= left
- cr
->base
.col
;
561 dat
.rinfo
.row_offset
= top
- cr
->base
.row
;
563 dat
.rinfo
.origin
= pt
->range
;
564 dat
.rinfo
.col_offset
= 0;
565 dat
.rinfo
.row_offset
= 0;
568 /* Move the styles on here so we get correct formats before recalc */
569 if (pt
->paste_flags
& PASTE_FORMATS
) {
570 if (pt
->paste_flags
& PASTE_TRANSPOSE
)
571 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
573 (sheet_style_set_list_cb_t
)
576 else if (pt
->paste_flags
& PASTE_FLIP_H
) {
577 int data
= 2 * left
+ src_cols
- 1;
578 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
580 (sheet_style_set_list_cb_t
)
581 range_flip_h
, &data
);
582 } else if (pt
->paste_flags
& PASTE_FLIP_V
) {
583 int data
= 2 * top
+ src_rows
- 1;
584 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
586 (sheet_style_set_list_cb_t
)
587 range_flip_v
, &data
);
589 sheet_style_set_list (pt
->sheet
, &dat
.top_left
,
590 cr
->styles
, NULL
, NULL
);
592 if (has_contents
&& !(pt
->paste_flags
& PASTE_DONT_MERGE
)) {
593 for (ptr
= cr
->merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
594 GnmRange tmp
= *((GnmRange
const *)ptr
->data
);
595 if (pt
->paste_flags
& PASTE_TRANSPOSE
) {
597 x
= tmp
.start
.col
; tmp
.start
.col
= tmp
.start
.row
; tmp
.start
.row
= x
;
598 x
= tmp
.end
.col
; tmp
.end
.col
= tmp
.end
.row
; tmp
.end
.row
= x
;
600 if (!range_translate (&tmp
, pt
->sheet
, left
, top
))
601 gnm_sheet_merge_add (pt
->sheet
, &tmp
, TRUE
, cc
);
605 if (has_contents
&& (pt
->paste_flags
& PASTE_LINK
)) {
606 paste_link (pt
, top
, left
, cr
);
610 if (has_contents
&& NULL
!= cr
->cell_content
) {
613 g_hash_table_foreach (cr
->cell_content
,
614 (GHFunc
)cb_paste_cell
, &dat
);
617 if (pt
->paste_flags
& (PASTE_COMMENTS
| PASTE_OBJECTS
))
618 for (ptr
= cr
->objects
; ptr
; ptr
= ptr
->next
)
619 paste_object (pt
, ptr
->data
, left
, top
);
622 if (!(pt
->paste_flags
& PASTE_NO_RECALC
)) {
624 sheet_region_queue_recalc (pt
->sheet
, r
);
625 sheet_flag_status_update_range (pt
->sheet
, r
);
627 sheet_flag_style_update_range (pt
->sheet
, r
);
629 sheet_range_calc_spans (pt
->sheet
, r
,
630 (pt
->paste_flags
& PASTE_FORMATS
) ? GNM_SPANCALC_RE_RENDER
: GNM_SPANCALC_RENDER
);
631 if (pt
->paste_flags
& PASTE_UPDATE_ROW_HEIGHT
)
632 rows_height_update (pt
->sheet
, &pt
->range
, FALSE
);
633 sheet_redraw_all (pt
->sheet
, FALSE
);
640 cb_clipboard_prepend_cell (GnmCellIter
const *iter
, GnmCellRegion
*cr
)
643 GnmCellCopy
*copy
= gnm_cell_copy_new (cr
,
644 iter
->pp
.eval
.col
- cr
->base
.col
,
645 iter
->pp
.eval
.row
- cr
->base
.row
);
646 copy
->val
= value_dup (iter
->cell
->value
);
648 if (gnm_cell_has_expr (iter
->cell
)) {
649 gnm_expr_top_ref (copy
->texpr
= iter
->cell
->base
.texpr
);
651 /* Check for array division */
652 if (!cr
->not_as_contents
&&
653 gnm_cell_array_bound (iter
->cell
, &a
) &&
654 (a
.start
.col
< cr
->base
.col
||
655 a
.start
.row
< cr
->base
.row
||
656 a
.end
.col
>= (cr
->base
.col
+ cr
->cols
) ||
657 a
.end
.row
>= (cr
->base
.row
+ cr
->rows
)))
658 cr
->not_as_contents
= TRUE
;
666 cb_dup_objects (SheetObject
const *src
, GnmCellRegion
*cr
)
668 SheetObject
*dst
= sheet_object_dup (src
);
670 SheetObjectAnchor tmp
= *sheet_object_get_anchor (src
);
671 range_translate (&tmp
.cell_bound
, sheet_object_get_sheet (src
),
672 - cr
->base
.col
, - cr
->base
.row
);
673 sheet_object_set_anchor (dst
, &tmp
);
674 cr
->objects
= g_slist_prepend (cr
->objects
, dst
);
679 * clipboard_copy_range:
681 * Entry point to the clipboard copy code
684 clipboard_copy_range (Sheet
*sheet
, GnmRange
const *r
)
687 GSList
*merged
, *ptr
;
690 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
691 g_return_val_if_fail (range_is_sane (r
), NULL
);
693 cr
= gnm_cell_region_new (sheet
);
695 cr
->cols
= range_width (r
);
696 cr
->rows
= range_height (r
);
697 cr
->col_state
= colrow_get_states (sheet
,
698 TRUE
, r
->start
.col
, r
->end
.col
);
699 cr
->row_state
= colrow_get_states (sheet
,
700 FALSE
, r
->start
.row
, r
->end
.row
);
702 sheet_foreach_cell_in_range ( sheet
, CELL_ITER_IGNORE_NONEXISTENT
,
703 r
->start
.col
, r
->start
.row
,
704 r
->end
.col
, r
->end
.row
,
705 (CellIterFunc
) cb_clipboard_prepend_cell
, cr
);
706 objects
= sheet_objects_get (sheet
, r
, G_TYPE_NONE
);
707 g_slist_foreach (objects
, (GFunc
)cb_dup_objects
, cr
);
708 g_slist_free (objects
);
710 cr
->styles
= sheet_style_get_range (sheet
, r
);
712 merged
= gnm_sheet_merge_get_overlap (sheet
, r
);
713 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
714 GnmRange
*tmp
= gnm_range_dup (ptr
->data
);
715 range_translate (tmp
, sheet
, -r
->start
.col
, -r
->start
.row
);
716 cr
->merged
= g_slist_prepend (cr
->merged
, tmp
);
718 g_slist_free (merged
);
724 cb_clipboard_copy_range_undo (GnmCellRegion
*cr
, GnmSheetRange
*sr
,
729 clipboard_paste_region
731 paste_target_init (&pt
,
734 PASTE_CONTENTS
| PASTE_FORMATS
|
735 PASTE_OBJECTS
| PASTE_COMMENTS
),
740 * clipboard_copy_range_undo:
744 * Returns: (transfer full): A #GOUndo object that will restore the contents
745 * of the given range.
748 clipboard_copy_range_undo (Sheet
*sheet
, GnmRange
const *r
)
750 GnmCellRegion
*cr
= clipboard_copy_range (sheet
, r
);
751 g_return_val_if_fail (cr
!= NULL
, NULL
);
752 return go_undo_binary_new (cr
, gnm_sheet_range_new (sheet
, r
),
753 (GOUndoBinaryFunc
)cb_clipboard_copy_range_undo
,
754 (GFreeFunc
)cellregion_unref
,
759 * clipboard_copy_ranges_undo:
761 * @ranges: (element-type GnmRange) (transfer none): list of ranges
763 * Returns: (transfer full): A #GOUndo object that will restore the contents
764 * of the given range.
767 clipboard_copy_ranges_undo (Sheet
*sheet
, GSList
*ranges
)
772 for (l
= ranges
; l
!= NULL
; l
= l
->next
) {
773 GnmRange
*r
= l
->data
;
774 GOUndo
*undo1
= clipboard_copy_range_undo (sheet
, r
);
775 undo
= go_undo_combine (undo
, undo1
);
783 * clipboard_copy_obj:
785 * @objects: (element-type SheetObject): #GSList
787 * Returns a cell region with copies of objects in list. Caller is responsible
788 * for cellregion_unref-ing the result.
791 clipboard_copy_obj (Sheet
*sheet
, GSList
*objects
)
793 SheetObjectAnchor tmp_anchor
;
794 SheetObjectAnchor
const *anchor
;
802 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
803 g_return_val_if_fail (objects
!= NULL
, NULL
);
805 cr
= gnm_cell_region_new (sheet
);
806 for (ptr
= objects
; ptr
!= NULL
; ptr
= ptr
->next
)
807 if (NULL
!= (so
= sheet_object_dup (ptr
->data
))) {
808 anchor
= sheet_object_get_anchor (so
);
810 #warning FIXME : This is only used in gnm_sog_write_image
811 /* NOTE #1 : It seems necessary to handle pasting an object that has been removed from
812 * the sheet after being added to the clipboard. it seems like we would need
813 * this sort of information for anything that implements SheetObjectImageableIface
815 sheet_object_anchor_to_pts (anchor
, sheet
, coords
);
816 w
= fabs (coords
[2] - coords
[0]) + 1.5;
817 h
= fabs (coords
[3] - coords
[1]) + 1.5;
818 g_object_set_data (G_OBJECT (so
), "pt-width-at-copy",
819 GUINT_TO_POINTER (w
));
820 g_object_set_data (G_OBJECT (so
), "pt-height-at-copy",
821 GUINT_TO_POINTER (h
));
823 tmp_anchor
= *anchor
;
824 r
= &tmp_anchor
.cell_bound
;
825 range_translate (r
, sheet
,
826 -MIN (r
->start
.col
, r
->end
.col
),
827 -MIN (r
->start
.row
, r
->end
.row
));
828 sheet_object_set_anchor (so
, &tmp_anchor
);
830 cr
->objects
= g_slist_prepend (cr
->objects
, so
);
837 paste_target_init (GnmPasteTarget
*pt
, Sheet
*sheet
, GnmRange
const *r
, int flags
)
841 pt
->paste_flags
= flags
;
846 * gnm_cell_region_new :
847 * @origin_sheet: optionally NULL.
849 * A convenience routine to create CellRegions and init the flags nicely.
852 gnm_cell_region_new (Sheet
*origin_sheet
)
854 GnmCellRegion
*cr
= g_new0 (GnmCellRegion
, 1);
855 cr
->origin_sheet
= origin_sheet
;
856 cr
->date_conv
= origin_sheet
&& origin_sheet
->workbook
857 ? workbook_date_conv (origin_sheet
->workbook
)
859 cr
->cols
= cr
->rows
= -1;
860 cr
->not_as_contents
= FALSE
;
861 cr
->cell_content
= NULL
;
862 cr
->col_state
= NULL
;
863 cr
->row_state
= NULL
;
873 cellregion_ref (GnmCellRegion
*cr
)
875 g_return_if_fail (cr
!= NULL
);
880 cellregion_unref (GnmCellRegion
*cr
)
882 g_return_if_fail (cr
!= NULL
);
884 if (cr
->ref_count
> 1) {
889 if (NULL
!= cr
->cell_content
) {
890 g_hash_table_destroy (cr
->cell_content
);
891 cr
->cell_content
= NULL
;
894 if (NULL
!= cr
->col_state
)
895 cr
->col_state
= colrow_state_list_destroy (cr
->col_state
);
896 if (NULL
!= cr
->row_state
)
897 cr
->row_state
= colrow_state_list_destroy (cr
->row_state
);
898 if (cr
->styles
!= NULL
) {
899 style_list_free (cr
->styles
);
902 if (cr
->merged
!= NULL
) {
904 for (ptr
= cr
->merged
; ptr
!= NULL
; ptr
= ptr
->next
)
906 g_slist_free (cr
->merged
);
909 if (cr
->objects
!= NULL
) {
911 for (ptr
= cr
->objects
; ptr
!= NULL
; ptr
= ptr
->next
)
912 g_object_unref (ptr
->data
);
913 g_slist_free (cr
->objects
);
921 gnm_cell_region_get_type (void)
926 t
= g_boxed_type_register_static ("GnmCellRegion",
927 (GBoxedCopyFunc
)cellregion_ref
,
928 (GBoxedFreeFunc
)cellregion_unref
);
934 cellregion_get_content (GnmCellRegion
const *cr
, int col
, int row
)
936 if (cr
->cell_content
) {
940 return g_hash_table_lookup (cr
->cell_content
, &pos
);
946 cb_invalidate_cellcopy (GnmCellCopy
*cc
, gconstpointer ignore
,
947 GnmExprRelocateInfo
*rinfo
)
949 GnmExprTop
const *texpr
;
950 if (NULL
!= cc
->texpr
) {
951 texpr
= gnm_expr_top_relocate (cc
->texpr
, rinfo
, FALSE
);
953 gnm_expr_top_unref (cc
->texpr
);
960 * cellregion_invalidate_sheet :
961 * @cr: #GnmCellRegion
964 * Invalidate references from cell content, objects or style to @sheet.
967 cellregion_invalidate_sheet (GnmCellRegion
*cr
,
971 gboolean save_invalidated
;
972 GnmExprRelocateInfo rinfo
;
974 g_return_if_fail (cr
!= NULL
);
975 g_return_if_fail (IS_SHEET (sheet
));
977 save_invalidated
= sheet
->being_invalidated
;
978 sheet
->being_invalidated
= TRUE
;
980 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_INVALIDATE_SHEET
;
981 if (NULL
!= cr
->cell_content
)
982 g_hash_table_foreach (cr
->cell_content
,
983 (GHFunc
)cb_invalidate_cellcopy
, &rinfo
);
984 sheet
->being_invalidated
= save_invalidated
;
986 for (ptr
= cr
->objects
; ptr
!= NULL
; ptr
= ptr
->next
)
987 sheet_object_invalidate_sheet (ptr
->data
, sheet
);
989 if (cr
->origin_sheet
== sheet
)
990 cr
->origin_sheet
= NULL
;
994 cb_cellregion_extent (GnmCellCopy
*cc
, gconstpointer ignore
, GnmRange
*extent
)
996 if (extent
->start
.col
>= 0) {
997 if (extent
->start
.col
> cc
->offset
.col
)
998 extent
->start
.col
= cc
->offset
.col
;
999 else if (extent
->end
.col
< cc
->offset
.col
)
1000 extent
->end
.col
= cc
->offset
.col
;
1002 if (extent
->start
.row
> cc
->offset
.row
)
1003 extent
->start
.row
= cc
->offset
.row
;
1004 else if (extent
->end
.row
< cc
->offset
.row
)
1005 extent
->end
.row
= cc
->offset
.row
;
1006 } else /* first cell */
1007 extent
->start
= extent
->end
= cc
->offset
;
1011 * cellregion_extent:
1012 * @cr: #GnmCellRegion
1013 * @extent: #GnmRange
1015 * Find the min and max col/row with cell content
1018 cellregion_extent (GnmCellRegion
const *cr
, GnmRange
*extent
)
1020 if (NULL
!= cr
->cell_content
) {
1021 range_init (extent
, -1, -1, -1, -1);
1022 g_hash_table_foreach (cr
->cell_content
,
1023 (GHFunc
)cb_cellregion_extent
, extent
);
1025 range_init (extent
, 0, 0, 0, 0);
1029 cellregion_to_string (GnmCellRegion
const *cr
,
1030 gboolean only_visible
,
1031 GODateConventions
const *date_conv
)
1033 GString
*all
, *line
;
1034 GnmCellCopy
const *cc
;
1035 int col
, row
, next_col_check
, next_row_check
;
1037 ColRowStateList
const *col_state
= NULL
, *row_state
= NULL
;
1038 ColRowRLEState
const *rle
;
1040 GnmStyle
const *style
;
1041 GOFormat
const *fmt
;
1043 g_return_val_if_fail (cr
!= NULL
, NULL
);
1044 g_return_val_if_fail (cr
->rows
>= 0, NULL
);
1045 g_return_val_if_fail (cr
->cols
>= 0, NULL
);
1047 /* pre-allocate rough approximation of buffer */
1048 ncells
= cr
->cell_content
? g_hash_table_size (cr
->cell_content
) : 0;
1049 all
= g_string_sized_new (20 * ncells
+ 1);
1050 line
= g_string_new (NULL
);
1052 cellregion_extent (cr
, &extent
);
1054 if (only_visible
&& NULL
!= (row_state
= cr
->row_state
)) {
1055 next_row_check
= i
= 0;
1056 while ((i
+= ((ColRowRLEState
*)(row_state
->data
))->length
) <= extent
.start
.row
) {
1057 if (NULL
== (row_state
= row_state
->next
)) {
1058 next_row_check
= gnm_sheet_get_max_rows (cr
->origin_sheet
);
1064 next_row_check
= gnm_sheet_get_max_rows (cr
->origin_sheet
);
1066 for (row
= extent
.start
.row
; row
<= extent
.end
.row
;) {
1067 if (row
>= next_row_check
) {
1068 rle
= row_state
->data
;
1069 row_state
= row_state
->next
;
1070 next_row_check
+= rle
->length
;
1071 if (!rle
->state
.visible
) {
1072 row
= next_row_check
;
1077 g_string_assign (line
, "");
1079 if (only_visible
&& NULL
!= (col_state
= cr
->col_state
)) {
1080 next_col_check
= i
= 0;
1081 while ((i
+= ((ColRowRLEState
*)(col_state
->data
))->length
) <= extent
.start
.col
) {
1082 if (NULL
== (col_state
= col_state
->next
)) {
1083 next_col_check
= gnm_sheet_get_max_cols (cr
->origin_sheet
);
1089 next_col_check
= gnm_sheet_get_max_cols (cr
->origin_sheet
);
1091 for (col
= extent
.start
.col
; col
<= extent
.end
.col
;) {
1092 if (col
== next_col_check
) {
1093 rle
= col_state
->data
;
1094 col_state
= col_state
->next
;
1095 next_col_check
+= rle
->length
;
1096 if (!rle
->state
.visible
) {
1097 col
= next_col_check
;
1102 cc
= cellregion_get_content (cr
, col
, row
);
1104 style
= style_list_get_style (cr
->styles
, col
, row
);
1105 fmt
= gnm_style_get_format (style
);
1107 if (go_format_is_general (fmt
) &&
1108 VALUE_FMT (cc
->val
))
1109 fmt
= VALUE_FMT (cc
->val
);
1111 format_value_gstring (line
, fmt
, cc
->val
,
1114 if (++col
<= extent
.end
.col
)
1115 g_string_append_c (line
, '\t');
1117 g_string_append_len (all
, line
->str
, line
->len
);
1118 if (++row
<= extent
.end
.row
)
1119 g_string_append_c (all
, '\n');
1122 g_string_free (line
, TRUE
);
1127 cellregion_cmd_size (GnmCellRegion
const *cr
)
1131 g_return_val_if_fail (cr
!= NULL
, 1);
1133 res
+= g_slist_length (cr
->styles
);
1134 if (NULL
!= cr
->cell_content
)
1135 res
+= g_hash_table_size (cr
->cell_content
);
1140 gnm_cell_copy_free (GnmCellCopy
*cc
)
1143 gnm_expr_top_unref (cc
->texpr
);
1146 value_release (cc
->val
);
1149 CHUNK_FREE (cell_copy_pool
, cc
);
1153 gnm_cell_copy_new (GnmCellRegion
*cr
, int col_offset
, int row_offset
)
1155 GnmCellCopy
*res
= CHUNK_ALLOC (GnmCellCopy
, cell_copy_pool
);
1156 ((GnmCellPos
*)(&res
->offset
))->col
= col_offset
;
1157 ((GnmCellPos
*)(&res
->offset
))->row
= row_offset
;
1161 if (NULL
== cr
->cell_content
)
1162 cr
->cell_content
= g_hash_table_new_full (
1163 (GHashFunc
)&gnm_cellpos_hash
,
1164 (GCompareFunc
)&gnm_cellpos_equal
,
1165 (GDestroyNotify
) gnm_cell_copy_free
,
1168 g_hash_table_insert (cr
->cell_content
, res
, res
);
1174 clipboard_init (void)
1176 #if USE_CELL_COPY_POOLS
1178 go_mem_chunk_new ("cell copy pool",
1179 sizeof (GnmCellCopy
),
1184 #if USE_CELL_COPY_POOLS
1186 cb_cell_copy_pool_leak (gpointer data
, G_GNUC_UNUSED gpointer user
)
1188 GnmCellCopy
const *cc
= data
;
1189 g_printerr ("Leaking cell copy at %p.\n", (void *)cc
);
1194 clipboard_shutdown (void)
1196 #if USE_CELL_COPY_POOLS
1197 go_mem_chunk_foreach_leak (cell_copy_pool
, cb_cell_copy_pool_leak
, NULL
);
1198 go_mem_chunk_destroy (cell_copy_pool
, FALSE
);
1199 cell_copy_pool
= NULL
;