GETENV: check for proper UTF-8.
[gnumeric.git] / src / clipboard.c
blob4bf64b4a18b48127e4c0175a6ca3d30b04eee59a
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 GnmPasteTarget *res = g_new (GnmPasteTarget, 1);
90 memcpy (res, pt, sizeof (GnmPasteTarget));
91 return res;
94 GType
95 gnm_paste_target_get_type (void)
97 static GType t = 0;
99 if (t == 0) {
100 t = g_boxed_type_register_static ("GnmPasteTarget",
101 (GBoxedCopyFunc)gnm_paste_target_copy,
102 (GBoxedFreeFunc)g_free);
104 return t;
107 static gboolean
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)
118 if (texpr)
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));
124 return NULL;
127 static GnmExprOp
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;
141 return 0;
144 static void
145 paste_cell_with_operation (Sheet *dst_sheet,
146 int target_col, int target_row,
147 GnmExprRelocateInfo const *rinfo,
148 GnmCellCopy const *src,
149 int paste_flags)
151 GnmCell *dst;
152 GnmExprOp op;
154 if (src->texpr == NULL &&
155 !VALUE_IS_EMPTY (src->val) &&
156 !VALUE_IS_NUMBER (src->val))
157 return;
159 dst = sheet_cell_fetch (dst_sheet, target_col, target_row);
160 if (!cell_has_expr_or_number_or_blank (dst))
161 return;
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);
171 if (relo) {
172 gnm_cell_set_expr (dst, relo);
173 gnm_expr_top_unref (relo);
174 } else
175 gnm_cell_set_expr (dst, res);
176 gnm_expr_top_unref (res);
177 } else {
178 GnmValue *value;
179 GnmEvalPos pos;
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
196 * this is called.
198 static void
199 paste_link (GnmPasteTarget const *pt, int top, int left,
200 GnmCellRegion const *cr)
202 GnmCellPos pos;
203 GnmCellRef source_cell_ref;
204 int x, y;
206 /* Not possible to link to arbitrary (non gnumeric) sources yet. */
207 /* TODO : eventually support interprocess gnumeric links */
208 if (cr->origin_sheet == NULL)
209 return;
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;
216 pos.col = left;
217 for (x = 0 ; x < cr->cols ; x++, pos.col++) {
218 source_cell_ref.col = cr->base.col + x;
219 pos.row = top;
220 for (y = 0 ; y < cr->rows ; y++, pos.row++) {
221 GnmExprTop const *texpr;
222 GnmCell *cell =
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))
228 continue;
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;
240 GnmCellPos top_left;
241 GnmExprRelocateInfo rinfo;
242 gboolean translate_dates;
246 * paste_cell:
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.
254 static void
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);
265 else {
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);
273 if (trelo) {
274 if (relo)
275 gnm_expr_top_unref (relo);
276 relo = trelo;
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);
284 if (NULL != relo)
285 gnm_expr_top_unref (relo);
286 } else {
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)
292 ? 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),
297 dat->cr->date_conv,
298 workbook_date_conv (dst_sheet->workbook));
299 newval = value_new_float (fnew);
300 value_set_fmt (newval, VALUE_FMT (oldval));
304 if (!newval)
305 newval = value_dup (src->val);
306 gnm_cell_set_value (dst, newval);
311 static void
312 paste_object (GnmPasteTarget const *pt, SheetObject const *src, int left, int top)
314 SheetObject *dst;
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))
323 return;
324 } else if (!(pt->paste_flags & PASTE_OBJECTS))
325 return;
327 if (NULL == (dst = sheet_object_dup (src)))
328 return;
330 if (pt->paste_flags & PASTE_TRANSPOSE) {
331 GnmCellPos origin;
332 origin.col = 0;
333 origin.row = 0;
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);
342 static void
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;
358 } else {
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;
367 } else {
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);
375 static gboolean
376 range_flip_h (GnmRange *range, Sheet const *sheet, int const *data)
378 int t;
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;
386 return FALSE;
388 static gboolean
389 range_flip_v (GnmRange *range, Sheet const *sheet, int const *data)
391 int t;
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;
399 return FALSE;
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
411 * paste a region.
413 * returns : TRUE if there was a problem.
415 gboolean
416 clipboard_paste_region (GnmCellRegion const *cr,
417 GnmPasteTarget const *pt,
418 GOCmdContext *cc)
420 int repeat_horizontal, repeat_vertical, clearFlags;
421 int dst_cols, dst_rows, src_cols, src_rows;
422 int i, j;
423 GSList *ptr;
424 GnmRange const *r;
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);
441 return FALSE;
444 r = &pt->range;
445 dst_cols = range_width (r);
446 dst_rows = range_height (r);
447 src_cols = cr->cols;
448 src_rows = cr->rows;
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;
463 src_cols = 1;
464 src_rows = 1;
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) {
473 int tmp = src_cols;
474 src_cols = src_rows;
475 src_rows = tmp;
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."));
482 return TRUE;
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."),
491 dst_cols, src_cols);
492 go_cmd_context_error_invalid (cc, _("Unable to paste"), msg);
493 g_free (msg);
494 return TRUE;
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."),
502 dst_rows, src_rows);
503 go_cmd_context_error_invalid (cc, _("Unable to paste"), msg);
504 g_free (msg);
505 return TRUE;
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"));
513 return TRUE;
516 clearFlags = 0;
517 /* clear the region where we will paste */
518 if (has_contents)
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))
528 clearFlags = 0;
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,
538 dst_col, dst_row,
539 dst_col + dst_cols - 1,
540 dst_row + dst_rows - 1,
541 clearFlags, cc);
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;
562 } else {
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,
572 cr->styles,
573 (sheet_style_set_list_cb_t)
574 range_transpose,
575 &dat.top_left);
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,
579 cr->styles,
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,
585 cr->styles,
586 (sheet_style_set_list_cb_t)
587 range_flip_v, &data);
588 } else
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) {
596 int x;
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);
607 continue;
610 if (has_contents && NULL != cr->cell_content) {
611 dat.pt = pt;
612 dat.cr = cr;
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)) {
623 if (has_contents) {
624 sheet_region_queue_recalc (pt->sheet, r);
625 sheet_flag_status_update_range (pt->sheet, r);
626 } else
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);
636 return FALSE;
639 static GnmValue *
640 cb_clipboard_prepend_cell (GnmCellIter const *iter, GnmCellRegion *cr)
642 GnmRange a;
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;
659 } else
660 copy->texpr = NULL;
662 return NULL;
665 static void
666 cb_dup_objects (SheetObject const *src, GnmCellRegion *cr)
668 SheetObject *dst = sheet_object_dup (src);
669 if (dst != NULL) {
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
683 GnmCellRegion *
684 clipboard_copy_range (Sheet *sheet, GnmRange const *r)
686 GnmCellRegion *cr;
687 GSList *merged, *ptr;
688 GSList *objects;
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);
694 cr->base = r->start;
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);
720 return cr;
723 static void
724 cb_clipboard_copy_range_undo (GnmCellRegion *cr, GnmSheetRange *sr,
725 GOCmdContext *cc)
727 GnmPasteTarget pt;
729 clipboard_paste_region
730 (cr,
731 paste_target_init (&pt,
732 sr->sheet,
733 &sr->range,
734 PASTE_CONTENTS | PASTE_FORMATS |
735 PASTE_OBJECTS | PASTE_COMMENTS),
736 cc);
740 * clipboard_copy_range_undo:
741 * @sheet: #Sheet
742 * @r: #GnmRange
744 * Returns: (transfer full): A #GOUndo object that will restore the contents
745 * of the given range.
747 GOUndo *
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,
755 (GFreeFunc)g_free);
759 * clipboard_copy_ranges_undo:
760 * @sheet: #Sheet
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.
766 GOUndo *
767 clipboard_copy_ranges_undo (Sheet *sheet, GSList *ranges)
769 GSList *l;
770 GOUndo *undo = NULL;
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);
778 return undo;
783 * clipboard_copy_obj:
784 * @sheet: #Sheet
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.
790 GnmCellRegion *
791 clipboard_copy_obj (Sheet *sheet, GSList *objects)
793 SheetObjectAnchor tmp_anchor;
794 SheetObjectAnchor const *anchor;
795 GnmCellRegion *cr;
796 GnmRange *r;
797 GSList *ptr;
798 SheetObject *so;
799 double coords [4];
800 guint w, h;
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);
833 return cr;
836 GnmPasteTarget*
837 paste_target_init (GnmPasteTarget *pt, Sheet *sheet, GnmRange const *r, int flags)
839 pt->sheet = sheet;
840 pt->range = *r;
841 pt->paste_flags = flags;
842 return pt;
846 * gnm_cell_region_new :
847 * @origin_sheet: optionally NULL.
849 * A convenience routine to create CellRegions and init the flags nicely.
851 GnmCellRegion *
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)
858 : NULL;
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;
864 cr->styles = NULL;
865 cr->merged = NULL;
866 cr->objects = NULL;
867 cr->ref_count = 1;
869 return cr;
872 void
873 cellregion_ref (GnmCellRegion *cr)
875 g_return_if_fail (cr != NULL);
876 cr->ref_count++;
879 void
880 cellregion_unref (GnmCellRegion *cr)
882 g_return_if_fail (cr != NULL);
884 if (cr->ref_count > 1) {
885 cr->ref_count--;
886 return;
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);
900 cr->styles = NULL;
902 if (cr->merged != NULL) {
903 GSList *ptr;
904 for (ptr = cr->merged; ptr != NULL ; ptr = ptr->next)
905 g_free (ptr->data);
906 g_slist_free (cr->merged);
907 cr->merged = NULL;
909 if (cr->objects != NULL) {
910 GSList *ptr;
911 for (ptr = cr->objects; ptr != NULL ; ptr = ptr->next)
912 g_object_unref (ptr->data);
913 g_slist_free (cr->objects);
914 cr->objects = NULL;
917 g_free (cr);
920 GType
921 gnm_cell_region_get_type (void)
923 static GType t = 0;
925 if (t == 0) {
926 t = g_boxed_type_register_static ("GnmCellRegion",
927 (GBoxedCopyFunc)cellregion_ref,
928 (GBoxedFreeFunc)cellregion_unref);
930 return t;
933 static GnmCellCopy *
934 cellregion_get_content (GnmCellRegion const *cr, int col, int row)
936 if (cr->cell_content) {
937 GnmCellPos pos;
938 pos.col = col;
939 pos.row = row;
940 return g_hash_table_lookup (cr->cell_content, &pos);
941 } else
942 return NULL;
945 static void
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);
952 if (NULL != texpr) {
953 gnm_expr_top_unref (cc->texpr);
954 cc->texpr = texpr;
960 * cellregion_invalidate_sheet :
961 * @cr: #GnmCellRegion
962 * @sheet: #Sheet
964 * Invalidate references from cell content, objects or style to @sheet.
966 void
967 cellregion_invalidate_sheet (GnmCellRegion *cr,
968 Sheet *sheet)
970 GSList *ptr;
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;
993 static void
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
1017 static void
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);
1024 } else
1025 range_init (extent, 0, 0, 0, 0);
1028 GString *
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;
1036 GnmRange extent;
1037 ColRowStateList const *col_state = NULL, *row_state = NULL;
1038 ColRowRLEState const *rle;
1039 int ncells, i;
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);
1059 break;
1061 next_row_check = i;
1063 } else
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;
1073 continue;
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);
1084 break;
1086 next_col_check = i;
1088 } else
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;
1098 continue;
1102 cc = cellregion_get_content (cr, col, row);
1103 if (cc) {
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,
1112 -1, date_conv);
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);
1123 return all;
1127 cellregion_cmd_size (GnmCellRegion const *cr)
1129 int res = 1;
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);
1136 return res;
1139 static void
1140 gnm_cell_copy_free (GnmCellCopy *cc)
1142 if (cc->texpr) {
1143 gnm_expr_top_unref (cc->texpr);
1144 cc->texpr = NULL;
1146 value_release (cc->val);
1147 cc->val = NULL;
1149 CHUNK_FREE (cell_copy_pool, cc);
1152 GnmCellCopy *
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;
1158 res->texpr = NULL;
1159 res->val = NULL;
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,
1166 NULL);
1168 g_hash_table_insert (cr->cell_content, res, res);
1170 return res;
1173 void
1174 clipboard_init (void)
1176 #if USE_CELL_COPY_POOLS
1177 cell_copy_pool =
1178 go_mem_chunk_new ("cell copy pool",
1179 sizeof (GnmCellCopy),
1180 4 * 1024 - 128);
1181 #endif
1184 #if USE_CELL_COPY_POOLS
1185 static void
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);
1191 #endif
1193 void
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;
1200 #endif