Sheet: add new sheet_date_conv convenience function.
[gnumeric.git] / src / clipboard.c
blobcc597a044a7d995af1833273437ab3004fc704f2
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
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
21 * USA
23 #include <gnumeric-config.h>
24 #include "gnumeric.h"
25 #include "clipboard.h"
27 #include "sheet.h"
28 #include "cell.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"
35 #include "workbook.h"
36 #include "ranges.h"
37 #include "colrow.h"
38 #include "expr.h"
39 #include "value.h"
40 #include "mstyle.h"
41 #include "stf-parse.h"
42 #include "gnm-format.h"
43 #include "sheet-object-cell-comment.h"
45 #include <glib/gi18n-lib.h>
46 #include <locale.h>
47 #include <string.h>
48 #include <goffice/goffice.h>
50 #ifndef USE_CELL_COPY_POOLS
51 #define USE_CELL_COPY_POOLS 1
52 #endif
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))
59 #else
60 #define CHUNK_ALLOC(T,c) g_new (T,1)
61 #define CHUNK_FREE(p,v) g_free ((v))
62 #endif
64 /* creating a boxed type for GnmCellCopy (needed by introspection) */
65 static gpointer
66 pointer_dup (gpointer *cc)
68 return cc;
71 GType
72 gnm_cell_copy_get_type (void)
74 static GType t = 0;
76 if (t == 0) {
77 t = g_boxed_type_register_static ("GnmCellCopy",
78 (GBoxedCopyFunc)pointer_dup,
79 (GBoxedFreeFunc)pointer_dup);
81 return t;
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));
92 GType
93 gnm_paste_target_get_type (void)
95 static GType t = 0;
97 if (t == 0) {
98 t = g_boxed_type_register_static ("GnmPasteTarget",
99 (GBoxedCopyFunc)gnm_paste_target_copy,
100 (GBoxedFreeFunc)g_free);
102 return t;
105 GnmPasteTarget *
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);
110 return res;
114 static gboolean
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)
125 if (texpr)
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));
131 return NULL;
134 static GnmExprOp
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;
148 return 0;
151 static void
152 paste_cell_with_operation (Sheet *dst_sheet,
153 int target_col, int target_row,
154 GnmExprRelocateInfo const *rinfo,
155 GnmCellCopy const *src,
156 int paste_flags)
158 GnmCell *dst;
159 GnmExprOp op;
161 if (src->texpr == NULL &&
162 !VALUE_IS_EMPTY (src->val) &&
163 !VALUE_IS_NUMBER (src->val))
164 return;
166 dst = sheet_cell_fetch (dst_sheet, target_col, target_row);
167 if (!cell_has_expr_or_number_or_blank (dst))
168 return;
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);
178 if (relo) {
179 gnm_cell_set_expr (dst, relo);
180 gnm_expr_top_unref (relo);
181 } else
182 gnm_cell_set_expr (dst, res);
183 gnm_expr_top_unref (res);
184 } else {
185 GnmValue *value;
186 GnmEvalPos pos;
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
203 * this is called.
205 static void
206 paste_link (GnmPasteTarget const *pt, int top, int left,
207 GnmCellRegion const *cr)
209 GnmCellPos pos;
210 GnmCellRef source_cell_ref;
211 int x, y;
213 /* Not possible to link to arbitrary (non gnumeric) sources yet. */
214 /* TODO : eventually support interprocess gnumeric links */
215 if (cr->origin_sheet == NULL)
216 return;
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;
223 pos.col = left;
224 for (x = 0 ; x < cr->cols ; x++, pos.col++) {
225 source_cell_ref.col = cr->base.col + x;
226 pos.row = top;
227 for (y = 0 ; y < cr->rows ; y++, pos.row++) {
228 GnmExprTop const *texpr;
229 GnmCell *cell =
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))
235 continue;
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;
247 GnmCellPos top_left;
248 GnmExprRelocateInfo rinfo;
249 gboolean translate_dates;
253 * paste_cell:
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.
261 static void
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);
272 else {
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);
280 if (trelo) {
281 if (relo)
282 gnm_expr_top_unref (relo);
283 relo = trelo;
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);
291 if (NULL != relo)
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)
299 ? 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),
304 dat->cr->date_conv,
305 sheet_date_conv (dst_sheet));
306 newval = value_new_float (fnew);
307 value_set_fmt (newval, VALUE_FMT (oldval));
311 if (!newval)
312 newval = value_dup (src->val);
313 gnm_cell_set_value (dst, newval);
318 static void
319 paste_object (GnmPasteTarget const *pt, SheetObject const *src, int left, int top)
321 SheetObject *dst;
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))
330 return;
331 } else if (!(pt->paste_flags & PASTE_OBJECTS))
332 return;
334 if (NULL == (dst = sheet_object_dup (src)))
335 return;
337 if (pt->paste_flags & PASTE_TRANSPOSE) {
338 GnmCellPos origin;
339 origin.col = 0;
340 origin.row = 0;
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);
349 static void
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;
365 } else {
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;
374 } else {
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);
382 static gboolean
383 range_flip_h (GnmRange *range, Sheet const *sheet, int const *data)
385 int t;
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;
393 return FALSE;
395 static gboolean
396 range_flip_v (GnmRange *range, Sheet const *sheet, int const *data)
398 int t;
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;
406 return FALSE;
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
418 * paste a region.
420 * Returns: %TRUE if there was a problem.
422 gboolean
423 clipboard_paste_region (GnmCellRegion const *cr,
424 GnmPasteTarget const *pt,
425 GOCmdContext *cc)
427 int repeat_horizontal, repeat_vertical, clearFlags;
428 int dst_cols, dst_rows, src_cols, src_rows;
429 int i, j;
430 GSList *ptr;
431 GnmRange const *r;
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);
449 return FALSE;
452 r = &pt->range;
453 dst_cols = range_width (r);
454 dst_rows = range_height (r);
455 src_cols = cr->cols;
456 src_rows = cr->rows;
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;
469 src_cols = 1;
470 src_rows = 1;
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) {
479 int tmp = src_cols;
480 src_cols = src_rows;
481 src_rows = tmp;
484 if (cr->not_as_contents && (pt->paste_flags & PASTE_CONTENTS)) {
485 if (cc)
486 go_cmd_context_error_invalid
487 (cc,
488 _("Unable to paste"),
489 _("Contents can only be pasted by value or by link."));
490 return TRUE;
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."),
499 dst_cols, src_cols);
500 if (cc)
501 go_cmd_context_error_invalid (cc, _("Unable to paste"), msg);
502 g_free (msg);
503 return TRUE;
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."),
511 dst_rows, src_rows);
512 if (cc)
513 go_cmd_context_error_invalid (cc, _("Unable to paste"), msg);
514 g_free (msg);
515 return TRUE;
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)) {
520 if (cc)
521 go_cmd_context_error_invalid
522 (cc,
523 _("Unable to paste"),
524 _("result passes the sheet boundary"));
525 return TRUE;
528 clearFlags = 0;
529 /* clear the region where we will paste */
530 if (has_contents)
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))
540 clearFlags = 0;
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,
550 dst_col, dst_row,
551 dst_col + dst_cols - 1,
552 dst_row + dst_rows - 1,
553 clearFlags, cc);
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;
574 } else {
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,
584 cr->styles,
585 (sheet_style_set_list_cb_t)
586 range_transpose,
587 &dat.top_left);
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,
591 cr->styles,
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,
597 cr->styles,
598 (sheet_style_set_list_cb_t)
599 range_flip_v, &data);
600 } else
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) {
608 int x;
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);
619 continue;
622 if (has_contents && NULL != cr->cell_content) {
623 dat.pt = pt;
624 dat.cr = cr;
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;
635 do_col_widths =
636 no_flipping &&
637 ((pt->paste_flags & PASTE_COLUMN_WIDTHS) ||
638 ((pt->paste_flags & PASTE_COLUMN_WIDTHS_AUTO) &&
639 cr->origin_sheet &&
640 src_rows == gnm_sheet_get_max_rows (cr->origin_sheet)));
641 if (do_col_widths) {
642 int i;
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);
649 do_row_heights =
650 no_flipping &&
651 ((pt->paste_flags & PASTE_ROW_HEIGHTS) ||
652 ((pt->paste_flags & PASTE_ROW_HEIGHTS_AUTO) &&
653 cr->origin_sheet &&
654 src_cols == gnm_sheet_get_max_cols (cr->origin_sheet)));
655 if (do_row_heights) {
656 int i;
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)) {
664 if (has_contents) {
665 sheet_region_queue_recalc (pt->sheet, r);
666 sheet_flag_status_update_range (pt->sheet, r);
667 } else
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);
675 return FALSE;
678 static GnmValue *
679 cb_clipboard_prepend_cell (GnmCellIter const *iter, GnmCellRegion *cr)
681 GnmRange a;
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;
698 } else
699 copy->texpr = NULL;
701 return NULL;
704 static void
705 cb_dup_objects (SheetObject const *src, GnmCellRegion *cr)
707 SheetObject *dst = sheet_object_dup (src);
708 if (dst != NULL) {
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
722 GnmCellRegion *
723 clipboard_copy_range (Sheet *sheet, GnmRange const *r)
725 GnmCellRegion *cr;
726 GSList *merged, *ptr;
727 GSList *objects;
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);
733 cr->base = r->start;
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,
743 cr);
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);
758 return cr;
761 static void
762 cb_clipboard_copy_range_undo (GnmCellRegion *cr, GnmSheetRange *sr,
763 GOCmdContext *cc)
765 GnmPasteTarget pt;
767 clipboard_paste_region
768 (cr,
769 paste_target_init (&pt,
770 sr->sheet,
771 &sr->range,
772 PASTE_CONTENTS | PASTE_FORMATS |
773 PASTE_OBJECTS | PASTE_COMMENTS |
774 PASTE_COLUMN_WIDTHS | PASTE_ROW_HEIGHTS),
775 cc);
779 * clipboard_copy_range_undo:
780 * @sheet: #Sheet
781 * @r: #GnmRange
783 * Returns: (transfer full): A #GOUndo object that will restore the contents
784 * of the given range.
786 GOUndo *
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,
794 (GFreeFunc)g_free);
798 * clipboard_copy_ranges_undo:
799 * @sheet: #Sheet
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.
805 GOUndo *
806 clipboard_copy_ranges_undo (Sheet *sheet, GSList *ranges)
808 GSList *l;
809 GOUndo *undo = NULL;
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);
817 return undo;
822 * clipboard_copy_obj:
823 * @sheet: #Sheet
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.
829 GnmCellRegion *
830 clipboard_copy_obj (Sheet *sheet, GSList *objects)
832 SheetObjectAnchor tmp_anchor;
833 SheetObjectAnchor const *anchor;
834 GnmCellRegion *cr;
835 GnmRange *r;
836 GSList *ptr;
837 SheetObject *so;
838 double coords [4];
839 guint w, h;
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);
872 return cr;
875 GnmPasteTarget*
876 paste_target_init (GnmPasteTarget *pt, Sheet *sheet,
877 GnmRange const *r, GnmPasteFlags flags)
879 pt->sheet = sheet; // No ref
880 pt->range = *r;
881 pt->paste_flags = flags;
882 return pt;
886 * gnm_cell_region_new:
887 * @origin_sheet: optionally NULL.
889 * A convenience routine to create CellRegions and init the flags nicely.
891 GnmCellRegion *
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)
898 : NULL;
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;
904 cr->styles = NULL;
905 cr->merged = NULL;
906 cr->objects = NULL;
907 cr->ref_count = 1;
909 return cr;
912 GnmCellRegion *
913 cellregion_ref (GnmCellRegion *cr)
915 g_return_val_if_fail (cr != NULL, NULL);
916 cr->ref_count++;
917 return cr;
920 void
921 cellregion_unref (GnmCellRegion *cr)
923 g_return_if_fail (cr != NULL);
925 if (cr->ref_count > 1) {
926 cr->ref_count--;
927 return;
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);
941 cr->styles = NULL;
943 if (cr->merged != NULL) {
944 GSList *ptr;
945 for (ptr = cr->merged; ptr != NULL ; ptr = ptr->next)
946 g_free (ptr->data);
947 g_slist_free (cr->merged);
948 cr->merged = NULL;
950 if (cr->objects != NULL) {
951 GSList *ptr;
952 for (ptr = cr->objects; ptr != NULL ; ptr = ptr->next)
953 g_object_unref (ptr->data);
954 g_slist_free (cr->objects);
955 cr->objects = NULL;
958 g_free (cr);
961 GType
962 gnm_cell_region_get_type (void)
964 static GType t = 0;
966 if (t == 0) {
967 t = g_boxed_type_register_static ("GnmCellRegion",
968 (GBoxedCopyFunc)cellregion_ref,
969 (GBoxedFreeFunc)cellregion_unref);
971 return t;
974 static GnmCellCopy *
975 cellregion_get_content (GnmCellRegion const *cr, int col, int row)
977 if (cr->cell_content) {
978 GnmCellPos pos;
979 pos.col = col;
980 pos.row = row;
981 return g_hash_table_lookup (cr->cell_content, &pos);
982 } else
983 return NULL;
986 static void
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);
993 if (NULL != texpr) {
994 gnm_expr_top_unref (cc->texpr);
995 cc->texpr = texpr;
1001 * cellregion_invalidate_sheet:
1002 * @cr: #GnmCellRegion
1003 * @sheet: #Sheet
1005 * Invalidate references from cell content, objects or style to @sheet.
1007 void
1008 cellregion_invalidate_sheet (GnmCellRegion *cr,
1009 Sheet *sheet)
1011 GSList *ptr;
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;
1034 static void
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
1058 static void
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);
1065 } else
1066 range_init (extent, 0, 0, 0, 0);
1069 GString *
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;
1077 GnmRange extent;
1078 ColRowStateList const *col_state = NULL, *row_state = NULL;
1079 ColRowRLEState const *rle;
1080 int ncells, i;
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);
1100 break;
1102 next_row_check = i;
1104 } else
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;
1114 continue;
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);
1125 break;
1127 next_col_check = i;
1129 } else
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;
1139 continue;
1143 cc = cellregion_get_content (cr, col, row);
1144 if (cc) {
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,
1153 -1, date_conv);
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);
1164 return all;
1168 cellregion_cmd_size (GnmCellRegion const *cr)
1170 int res = 1;
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);
1177 return res;
1180 static void
1181 gnm_cell_copy_free (GnmCellCopy *cc)
1183 if (cc->texpr) {
1184 gnm_expr_top_unref (cc->texpr);
1185 cc->texpr = NULL;
1187 value_release (cc->val);
1188 cc->val = NULL;
1190 CHUNK_FREE (cell_copy_pool, cc);
1193 GnmCellCopy *
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;
1199 res->texpr = NULL;
1200 res->val = NULL;
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,
1207 NULL);
1209 g_hash_table_insert (cr->cell_content, res, res);
1211 return res;
1215 * clipboard_init: (skip)
1217 void
1218 clipboard_init (void)
1220 #if USE_CELL_COPY_POOLS
1221 cell_copy_pool =
1222 go_mem_chunk_new ("cell copy pool",
1223 sizeof (GnmCellCopy),
1224 4 * 1024 - 128);
1225 #endif
1228 #if USE_CELL_COPY_POOLS
1229 static void
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);
1235 #endif
1238 * clipboard_shutdown: (skip)
1240 void
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;
1247 #endif