Update Spanish translation
[gnumeric.git] / src / clipboard.c
blob683029a6ddf8e069bd137e64e75667019dc0da82
1 /*
2 * clipboard.c: A temporary store for contents from a worksheet
4 * Copyright (C) 2000-2008 Jody Goldberg (jody@gnome.org)
5 * 1999 Miguel de Icaza (miguel@gnu.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
22 #include <gnumeric-config.h>
23 #include <gnumeric.h>
24 #include <clipboard.h>
26 #include <sheet.h>
27 #include <cell.h>
28 #include <sheet-style.h>
29 #include <sheet-merge.h>
30 #include <dependent.h>
31 #include <selection.h>
32 #include <command-context.h>
33 #include <workbook-control.h>
34 #include <workbook.h>
35 #include <ranges.h>
36 #include <colrow.h>
37 #include <expr.h>
38 #include <value.h>
39 #include <mstyle.h>
40 #include <stf-parse.h>
41 #include <gnm-format.h>
42 #include <sheet-object-cell-comment.h>
44 #include <glib/gi18n-lib.h>
45 #include <locale.h>
46 #include <string.h>
47 #include <goffice/goffice.h>
49 #ifndef USE_CELL_COPY_POOLS
50 #define USE_CELL_COPY_POOLS 1
51 #endif
53 #if USE_CELL_COPY_POOLS
54 /* Memory pool for GnmCellCopy. */
55 static GOMemChunk *cell_copy_pool;
56 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
57 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
58 #else
59 #define CHUNK_ALLOC(T,c) g_new (T,1)
60 #define CHUNK_FREE(p,v) g_free ((v))
61 #endif
63 /* creating a boxed type for GnmCellCopy (needed by introspection) */
64 static gpointer
65 pointer_dup (gpointer *cc)
67 return cc;
70 GType
71 gnm_cell_copy_get_type (void)
73 static GType t = 0;
75 if (t == 0) {
76 t = g_boxed_type_register_static ("GnmCellCopy",
77 (GBoxedCopyFunc)pointer_dup,
78 (GBoxedFreeFunc)pointer_dup);
80 return t;
83 /* creating a boxed type for GnmPasteTarget (needed by introspection) */
85 static GnmPasteTarget *
86 gnm_paste_target_copy (GnmPasteTarget *pt)
88 return g_memdup (pt, sizeof (*pt));
91 GType
92 gnm_paste_target_get_type (void)
94 static GType t = 0;
96 if (t == 0) {
97 t = g_boxed_type_register_static ("GnmPasteTarget",
98 (GBoxedCopyFunc)gnm_paste_target_copy,
99 (GBoxedFreeFunc)g_free);
101 return t;
104 GnmPasteTarget *
105 gnm_paste_target_new (Sheet *sheet, GnmRange *r, GnmPasteFlags flags)
107 GnmPasteTarget *res = g_new (GnmPasteTarget, 1);
108 paste_target_init (res, sheet, r, flags);
109 return res;
113 static gboolean
114 cell_has_expr_or_number_or_blank (GnmCell const * cell)
116 return (gnm_cell_is_empty (cell) ||
117 (cell != NULL && gnm_cell_is_number (cell)) ||
118 (cell != NULL && gnm_cell_has_expr (cell)));
121 static GnmExpr const *
122 contents_as_expr (GnmExprTop const *texpr, GnmValue const *val)
124 if (texpr)
125 return gnm_expr_copy (texpr->expr);
126 if (VALUE_IS_EMPTY (val))
127 return gnm_expr_new_constant (value_new_float (0.0));
128 if (VALUE_IS_NUMBER (val))
129 return gnm_expr_new_constant (value_dup (val));
130 return NULL;
133 static GnmExprOp
134 paste_op_to_expr_op (int paste_flags)
136 g_return_val_if_fail (paste_flags & PASTE_OPER_MASK, 0);
138 if (paste_flags & PASTE_OPER_ADD)
139 return GNM_EXPR_OP_ADD;
140 else if (paste_flags & PASTE_OPER_SUB)
141 return GNM_EXPR_OP_SUB;
142 else if (paste_flags & PASTE_OPER_MULT)
143 return GNM_EXPR_OP_MULT;
144 else if (paste_flags & PASTE_OPER_DIV)
145 return GNM_EXPR_OP_DIV;
147 return 0;
150 static void
151 paste_cell_with_operation (Sheet *dst_sheet,
152 int target_col, int target_row,
153 GnmExprRelocateInfo const *rinfo,
154 GnmCellCopy const *src,
155 int paste_flags)
157 GnmCell *dst;
158 GnmExprOp op;
160 if (src->texpr == NULL &&
161 !VALUE_IS_EMPTY (src->val) &&
162 !VALUE_IS_NUMBER (src->val))
163 return;
165 dst = sheet_cell_fetch (dst_sheet, target_col, target_row);
166 if (!cell_has_expr_or_number_or_blank (dst))
167 return;
169 op = paste_op_to_expr_op (paste_flags);
170 /* FIXME : This does not handle arrays, linked cells, ranges, etc. */
171 if ((paste_flags & PASTE_CONTENTS) &&
172 (NULL != src->texpr || gnm_cell_has_expr (dst))) {
173 GnmExpr const *old_expr = contents_as_expr (dst->base.texpr, dst->value);
174 GnmExpr const *copied_expr = contents_as_expr (src->texpr, src->val);
175 GnmExprTop const *res = gnm_expr_top_new (gnm_expr_new_binary (old_expr, op, copied_expr));
176 GnmExprTop const *relo = gnm_expr_top_relocate (res, rinfo, FALSE);
177 if (relo) {
178 gnm_cell_set_expr (dst, relo);
179 gnm_expr_top_unref (relo);
180 } else
181 gnm_cell_set_expr (dst, res);
182 gnm_expr_top_unref (res);
183 } else {
184 GnmValue *value;
185 GnmEvalPos pos;
186 GnmExpr const *expr = gnm_expr_new_binary (
187 gnm_expr_new_constant (value_dup (dst->value)),
189 gnm_expr_new_constant (value_dup (src->val)));
190 GnmExprTop const *texpr = gnm_expr_top_new (expr);
192 eval_pos_init_cell (&pos, dst);
193 pos.dep = NULL; /* no dynamic deps */
194 value = gnm_expr_top_eval (texpr, &pos,
195 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
196 gnm_expr_top_unref (texpr);
197 gnm_cell_set_value (dst, value);
201 /* NOTE : Make sure to set up any merged regions in the target range BEFORE
202 * this is called.
204 static void
205 paste_link (GnmPasteTarget const *pt, int top, int left,
206 GnmCellRegion const *cr)
208 GnmCellPos pos;
209 GnmCellRef source_cell_ref;
210 int x, y;
212 /* Not possible to link to arbitrary (non gnumeric) sources yet. */
213 /* TODO : eventually support interprocess gnumeric links */
214 if (cr->origin_sheet == NULL)
215 return;
217 /* TODO : support relative links ? */
218 source_cell_ref.col_relative = 0;
219 source_cell_ref.row_relative = 0;
220 source_cell_ref.sheet = (cr->origin_sheet != pt->sheet)
221 ? cr->origin_sheet : NULL;
222 pos.col = left;
223 for (x = 0 ; x < cr->cols ; x++, pos.col++) {
224 source_cell_ref.col = cr->base.col + x;
225 pos.row = top;
226 for (y = 0 ; y < cr->rows ; y++, pos.row++) {
227 GnmExprTop const *texpr;
228 GnmCell *cell =
229 sheet_cell_fetch (pt->sheet, pos.col, pos.row);
231 /* This could easily be made smarter */
232 if (!gnm_cell_is_merged (cell) &&
233 gnm_sheet_merge_contains_pos (pt->sheet, &pos))
234 continue;
235 source_cell_ref.row = cr->base.row + y;
236 texpr = gnm_expr_top_new (gnm_expr_new_cellref (&source_cell_ref));
237 gnm_cell_set_expr (cell, texpr);
238 gnm_expr_top_unref (texpr);
243 struct paste_cell_data {
244 GnmPasteTarget const *pt;
245 GnmCellRegion const *cr;
246 GnmCellPos top_left;
247 GnmExprRelocateInfo rinfo;
248 gboolean translate_dates;
252 * paste_cell:
253 * @target_col: Column to put the cell into
254 * @target_row: Row to put the cell into.
255 * @src: A #GnmCellCopy with the content to paste
256 * @paste_flags: Bit mask that describes the paste options.
258 * Pastes a cell in the spreadsheet.
260 static void
261 paste_cell (int target_col, int target_row,
262 GnmCellCopy const *src,
263 const struct paste_cell_data *dat)
265 Sheet *dst_sheet = dat->pt->sheet;
266 int paste_flags = dat->pt->paste_flags;
268 if (paste_flags & PASTE_OPER_MASK)
269 paste_cell_with_operation (dst_sheet, target_col, target_row,
270 &dat->rinfo, src, paste_flags);
271 else {
272 GnmCell *dst = sheet_cell_fetch (dst_sheet, target_col, target_row);
273 if (NULL != src->texpr && (paste_flags & PASTE_CONTENTS)) {
274 GnmExprTop const *relo = gnm_expr_top_relocate (
275 src->texpr, &dat->rinfo, FALSE);
276 if (paste_flags & PASTE_TRANSPOSE) {
277 GnmExprTop const *trelo =
278 gnm_expr_top_transpose (relo ? relo : src->texpr);
279 if (trelo) {
280 if (relo)
281 gnm_expr_top_unref (relo);
282 relo = trelo;
284 } else if (!relo && gnm_expr_top_is_array_corner (src->texpr)) {
285 /* We must not share array expressions. */
286 relo = gnm_expr_top_new (gnm_expr_copy (src->texpr->expr));
288 gnm_cell_set_expr_and_value (dst, relo ? relo : src->texpr,
289 value_dup (src->val), TRUE);
290 if (NULL != relo)
291 gnm_expr_top_unref (relo);
292 } else if (src->val) {
293 GnmValue *newval = NULL;
294 GnmValue const *oldval = src->val;
296 if (dat->translate_dates && oldval && VALUE_IS_FLOAT (oldval)) {
297 GOFormat const *fmt = VALUE_FMT (oldval)
298 ? VALUE_FMT (oldval)
299 : gnm_style_get_format (gnm_cell_get_style (dst));
300 if (go_format_is_date (fmt) > 0) {
301 gnm_float fnew = go_date_conv_translate
302 (value_get_as_float (oldval),
303 dat->cr->date_conv,
304 sheet_date_conv (dst_sheet));
305 newval = value_new_float (fnew);
306 value_set_fmt (newval, VALUE_FMT (oldval));
310 if (!newval)
311 newval = value_dup (src->val);
312 gnm_cell_set_value (dst, newval);
317 static void
318 paste_object (GnmPasteTarget const *pt, SheetObject const *src, int left, int top)
320 SheetObject *dst;
321 SheetObjectAnchor tmp;
323 tmp = *sheet_object_get_anchor (src);
324 if (G_OBJECT_TYPE (src) == GNM_CELL_COMMENT_TYPE) {
325 if ((pt->paste_flags & PASTE_COMMENTS) &&
326 (pt->paste_flags & PASTE_IGNORE_COMMENTS_AT_ORIGIN &&
327 tmp.cell_bound.start.col == 0 &&
328 tmp.cell_bound.start.row == 0))
329 return;
330 } else if (!(pt->paste_flags & PASTE_OBJECTS))
331 return;
333 if (NULL == (dst = sheet_object_dup (src)))
334 return;
336 if (pt->paste_flags & PASTE_TRANSPOSE) {
337 GnmCellPos origin;
338 origin.col = 0;
339 origin.row = 0;
340 range_transpose (&tmp.cell_bound, pt->sheet, &origin);
342 range_translate (&tmp.cell_bound, pt->sheet, left, top);
343 sheet_object_set_anchor (dst, &tmp);
344 sheet_object_set_sheet (dst, pt->sheet);
345 g_object_unref (dst);
348 static void
349 cb_paste_cell (GnmCellCopy const *src, gconstpointer ignore,
350 struct paste_cell_data *dat)
352 int target_col = dat->top_left.col;
353 int target_row = dat->top_left.row;
355 if (dat->pt->paste_flags & PASTE_TRANSPOSE) {
356 target_col += src->offset.row;
357 target_row += src->offset.col;
358 } else if (dat->pt->paste_flags & PASTE_FLIP_H) {
359 target_col += dat->cr->cols - src->offset.col - 1;
360 target_row += src->offset.row;
361 } else if (dat->pt->paste_flags & PASTE_FLIP_V) {
362 target_col += src->offset.col;
363 target_row += dat->cr->rows - src->offset.row - 1;
364 } else {
365 target_col += src->offset.col;
366 target_row += src->offset.row;
369 dat->rinfo.pos.sheet = dat->pt->sheet;
370 if (dat->pt->paste_flags & PASTE_EXPR_LOCAL_RELOCATE) {
371 dat->rinfo.pos.eval.col = dat->cr->base.col + src->offset.col;
372 dat->rinfo.pos.eval.row = dat->cr->base.row + src->offset.row;
373 } else {
374 dat->rinfo.pos.eval.col = target_col;
375 dat->rinfo.pos.eval.row = target_row;
378 paste_cell (target_col, target_row, src, dat);
381 static gboolean
382 range_flip_h (GnmRange *range, Sheet const *sheet, int const *data)
384 int t;
386 g_return_val_if_fail (range != NULL, TRUE);
388 t = *data - range->end.col;
389 range->end.col = *data - range->start.col;
390 range->start.col = t;
392 return FALSE;
394 static gboolean
395 range_flip_v (GnmRange *range, Sheet const *sheet, int const *data)
397 int t;
399 g_return_val_if_fail (range != NULL, TRUE);
401 t = *data - range->end.row;
402 range->end.row = *data - range->start.row;
403 range->start.row = t;
405 return FALSE;
409 * clipboard_paste_region:
410 * @cr: The GnmCellRegion to paste.
411 * @pt: Where to paste the values.
412 * @cc: (nullable): The context for error handling.
414 * Pastes the supplied GnmCellRegion (@cr) into the supplied
415 * GnmPasteTarget (@pt). This operation is not undoable. It does not auto grow
416 * the destination if the target is a singleton. This is a simple interface to
417 * paste a region.
419 * Returns: %TRUE if there was a problem.
421 gboolean
422 clipboard_paste_region (GnmCellRegion const *cr,
423 GnmPasteTarget const *pt,
424 GOCmdContext *cc)
426 int repeat_horizontal, repeat_vertical, clearFlags;
427 int dst_cols, dst_rows, src_cols, src_rows;
428 int i, j;
429 GSList *ptr;
430 GnmRange const *r;
431 gboolean has_contents, adjust_merges = TRUE;
432 struct paste_cell_data dat;
433 GnmRange const *merge_src;
434 gboolean no_flipping, do_col_widths, do_row_heights;
436 g_return_val_if_fail (pt != NULL, TRUE);
437 g_return_val_if_fail (cr != NULL, TRUE);
439 /* we do not need any of this fancy stuff when pasting a simple object */
440 if (cr->cell_content == NULL &&
441 cr->styles == NULL &&
442 cr->merged == NULL &&
443 cr->objects != NULL) {
444 if (pt->paste_flags & (PASTE_COMMENTS | PASTE_OBJECTS))
445 for (ptr = cr->objects; ptr; ptr = ptr->next)
446 paste_object (pt, ptr->data,
447 pt->range.start.col, pt->range.start.row);
448 return FALSE;
451 r = &pt->range;
452 dst_cols = range_width (r);
453 dst_rows = range_height (r);
454 src_cols = cr->cols;
455 src_rows = cr->rows;
457 /* If the source is a single cell or a single merge */
458 /* Treat a target of a single merge specially, don't split the merge */
459 if ((src_cols == 1 && src_rows == 1) ||
460 (g_slist_length (cr->merged) == 1 &&
461 (NULL != (merge_src = cr->merged->data)) &&
462 range_height (merge_src) == cr->rows &&
463 range_width (merge_src) == cr->cols)) {
464 GnmRange const *merge = gnm_sheet_merge_is_corner (pt->sheet, &r->start);
465 if (merge != NULL && range_equal (r, merge)) {
466 dst_cols = dst_rows = 1;
467 adjust_merges = FALSE;
468 src_cols = 1;
469 src_rows = 1;
471 /* Apparently links do not supercede merges */
472 } else if (pt->paste_flags & PASTE_LINK)
473 adjust_merges = FALSE;
475 has_contents = pt->paste_flags & (PASTE_CONTENTS|PASTE_AS_VALUES|PASTE_LINK);
477 if (pt->paste_flags & PASTE_TRANSPOSE) {
478 int tmp = src_cols;
479 src_cols = src_rows;
480 src_rows = tmp;
483 if (cr->not_as_contents && (pt->paste_flags & PASTE_CONTENTS)) {
484 if (cc)
485 go_cmd_context_error_invalid
486 (cc,
487 _("Unable to paste"),
488 _("Contents can only be pasted by value or by link."));
489 return TRUE;
492 /* calculate the tiling */
493 repeat_horizontal = dst_cols/src_cols;
494 if (repeat_horizontal * src_cols != dst_cols) {
495 char *msg = g_strdup_printf (
496 _("destination does not have an even multiple of source columns (%d vs %d)\n\n"
497 "Try selecting a single cell or an area of the same shape and size."),
498 dst_cols, src_cols);
499 if (cc)
500 go_cmd_context_error_invalid (cc, _("Unable to paste"), msg);
501 g_free (msg);
502 return TRUE;
505 repeat_vertical = dst_rows/src_rows;
506 if (repeat_vertical * src_rows != dst_rows) {
507 char *msg = g_strdup_printf (
508 _("destination does not have an even multiple of source rows (%d vs %d)\n\n"
509 "Try selecting a single cell or an area of the same shape and size."),
510 dst_rows, src_rows);
511 if (cc)
512 go_cmd_context_error_invalid (cc, _("Unable to paste"), msg);
513 g_free (msg);
514 return TRUE;
517 if ((pt->range.start.col + dst_cols) > gnm_sheet_get_max_cols (pt->sheet) ||
518 (pt->range.start.row + dst_rows) > gnm_sheet_get_max_rows (pt->sheet)) {
519 if (cc)
520 go_cmd_context_error_invalid
521 (cc,
522 _("Unable to paste"),
523 _("result passes the sheet boundary"));
524 return TRUE;
527 clearFlags = 0;
528 /* clear the region where we will paste */
529 if (has_contents)
530 clearFlags = CLEAR_VALUES | CLEAR_NORESPAN;
532 if (pt->paste_flags & PASTE_COMMENTS)
533 clearFlags |= CLEAR_COMMENTS;
535 /* No need to clear the formats. We will paste over top of these. */
536 /* if (pt->paste_flags & PASTE_FORMATS) clearFlags |= CLEAR_FORMATS; */
538 if (pt->paste_flags & (PASTE_OPER_MASK | PASTE_SKIP_BLANKS))
539 clearFlags = 0;
541 /* remove merged regions even for operations, or blanks */
542 if (has_contents && adjust_merges)
543 clearFlags |= CLEAR_MERGES;
545 if (clearFlags != 0) {
546 int const dst_col = pt->range.start.col;
547 int const dst_row = pt->range.start.row;
548 sheet_clear_region (pt->sheet,
549 dst_col, dst_row,
550 dst_col + dst_cols - 1,
551 dst_row + dst_rows - 1,
552 clearFlags, cc);
555 dat.translate_dates = cr->date_conv &&
556 !go_date_conv_equal (cr->date_conv, sheet_date_conv (pt->sheet));
558 for (i = 0; i < repeat_horizontal ; i++)
559 for (j = 0; j < repeat_vertical ; j++) {
560 int const left = i * src_cols + pt->range.start.col;
561 int const top = j * src_rows + pt->range.start.row;
563 dat.top_left.col = left;
564 dat.top_left.row = top;
565 dat.rinfo.reloc_type = GNM_EXPR_RELOCATE_MOVE_RANGE;
566 dat.rinfo.origin_sheet = dat.rinfo.target_sheet = pt->sheet;
567 if (pt->paste_flags & PASTE_EXPR_LOCAL_RELOCATE) {
568 dat.rinfo.origin.start = cr->base;
569 dat.rinfo.origin.end.col = cr->base.col + cr->cols - 1;
570 dat.rinfo.origin.end.row = cr->base.row + cr->rows - 1;
571 dat.rinfo.col_offset = left - cr->base.col;
572 dat.rinfo.row_offset = top - cr->base.row;
573 } else {
574 dat.rinfo.origin = pt->range;
575 dat.rinfo.col_offset = 0;
576 dat.rinfo.row_offset = 0;
579 /* Move the styles on here so we get correct formats before recalc */
580 if (pt->paste_flags & PASTE_FORMATS) {
581 if (pt->paste_flags & PASTE_TRANSPOSE)
582 sheet_style_set_list (pt->sheet, &dat.top_left,
583 cr->styles,
584 (sheet_style_set_list_cb_t)
585 range_transpose,
586 &dat.top_left);
587 else if (pt->paste_flags & PASTE_FLIP_H) {
588 int data = 2 * left + src_cols - 1;
589 sheet_style_set_list (pt->sheet, &dat.top_left,
590 cr->styles,
591 (sheet_style_set_list_cb_t)
592 range_flip_h, &data);
593 } else if (pt->paste_flags & PASTE_FLIP_V) {
594 int data = 2 * top + src_rows - 1;
595 sheet_style_set_list (pt->sheet, &dat.top_left,
596 cr->styles,
597 (sheet_style_set_list_cb_t)
598 range_flip_v, &data);
599 } else
600 sheet_style_set_list (pt->sheet, &dat.top_left,
601 cr->styles, NULL, NULL);
603 if (has_contents && !(pt->paste_flags & PASTE_DONT_MERGE)) {
604 for (ptr = cr->merged; ptr != NULL ; ptr = ptr->next) {
605 GnmRange tmp = *((GnmRange const *)ptr->data);
606 if (pt->paste_flags & PASTE_TRANSPOSE) {
607 int x;
608 x = tmp.start.col; tmp.start.col = tmp.start.row; tmp.start.row = x;
609 x = tmp.end.col; tmp.end.col = tmp.end.row; tmp.end.row = x;
611 if (!range_translate (&tmp, pt->sheet, left, top))
612 gnm_sheet_merge_add (pt->sheet, &tmp, TRUE, cc);
616 if (has_contents && (pt->paste_flags & PASTE_LINK)) {
617 paste_link (pt, top, left, cr);
618 continue;
621 if (has_contents && NULL != cr->cell_content) {
622 dat.pt = pt;
623 dat.cr = cr;
624 g_hash_table_foreach (cr->cell_content,
625 (GHFunc)cb_paste_cell, &dat);
628 if (pt->paste_flags & (PASTE_COMMENTS | PASTE_OBJECTS))
629 for (ptr = cr->objects; ptr; ptr = ptr->next)
630 paste_object (pt, ptr->data, left, top);
633 no_flipping = (pt->paste_flags & (PASTE_FLIP_H | PASTE_FLIP_V | PASTE_TRANSPOSE)) == 0;
634 do_col_widths =
635 no_flipping &&
636 ((pt->paste_flags & PASTE_COLUMN_WIDTHS) ||
637 ((pt->paste_flags & PASTE_COLUMN_WIDTHS_AUTO) &&
638 cr->origin_sheet &&
639 src_rows == gnm_sheet_get_max_rows (cr->origin_sheet)));
640 if (do_col_widths) {
641 int i;
642 for (i = 0; i < repeat_horizontal; i++) {
643 int first = pt->range.start.col + i * src_cols;
644 colrow_set_states (pt->sheet, TRUE, first, cr->col_state);
648 do_row_heights =
649 no_flipping &&
650 ((pt->paste_flags & PASTE_ROW_HEIGHTS) ||
651 ((pt->paste_flags & PASTE_ROW_HEIGHTS_AUTO) &&
652 cr->origin_sheet &&
653 src_cols == gnm_sheet_get_max_cols (cr->origin_sheet)));
654 if (do_row_heights) {
655 int i;
656 for (i = 0; i < repeat_vertical; i++) {
657 int first = pt->range.start.row + i * src_rows;
658 colrow_set_states (pt->sheet, FALSE, first, cr->row_state);
662 if (!(pt->paste_flags & PASTE_NO_RECALC)) {
663 if (has_contents) {
664 sheet_region_queue_recalc (pt->sheet, r);
665 sheet_flag_status_update_range (pt->sheet, r);
666 } else
667 sheet_flag_style_update_range (pt->sheet, r);
669 sheet_range_calc_spans (pt->sheet, r,
670 (pt->paste_flags & PASTE_FORMATS) ? GNM_SPANCALC_RE_RENDER : GNM_SPANCALC_RENDER);
671 sheet_redraw_all (pt->sheet, FALSE);
674 return FALSE;
677 static GnmValue *
678 cb_clipboard_prepend_cell (GnmCellIter const *iter, GnmCellRegion *cr)
680 GnmRange a;
681 GnmCellCopy *copy = gnm_cell_copy_new (cr,
682 iter->pp.eval.col - cr->base.col,
683 iter->pp.eval.row - cr->base.row);
684 copy->val = value_dup (iter->cell->value);
686 if (gnm_cell_has_expr (iter->cell)) {
687 gnm_expr_top_ref (copy->texpr = iter->cell->base.texpr);
689 /* Check for array division */
690 if (!cr->not_as_contents &&
691 gnm_cell_array_bound (iter->cell, &a) &&
692 (a.start.col < cr->base.col ||
693 a.start.row < cr->base.row ||
694 a.end.col >= (cr->base.col + cr->cols) ||
695 a.end.row >= (cr->base.row + cr->rows)))
696 cr->not_as_contents = TRUE;
697 } else
698 copy->texpr = NULL;
700 return NULL;
703 static void
704 cb_dup_objects (SheetObject const *src, GnmCellRegion *cr)
706 SheetObject *dst = sheet_object_dup (src);
707 if (dst != NULL) {
708 SheetObjectAnchor tmp = *sheet_object_get_anchor (src);
709 range_translate (&tmp.cell_bound, sheet_object_get_sheet (src),
710 - cr->base.col, - cr->base.row);
711 sheet_object_set_anchor (dst, &tmp);
712 cr->objects = g_slist_prepend (cr->objects, dst);
717 * clipboard_copy_range:
719 * Entry point to the clipboard copy code
721 GnmCellRegion *
722 clipboard_copy_range (Sheet *sheet, GnmRange const *r)
724 GnmCellRegion *cr;
725 GSList *merged, *ptr;
726 GSList *objects;
728 g_return_val_if_fail (IS_SHEET (sheet), NULL);
729 g_return_val_if_fail (range_is_sane (r), NULL);
731 cr = gnm_cell_region_new (sheet);
732 cr->base = r->start;
733 cr->cols = range_width (r);
734 cr->rows = range_height (r);
735 cr->col_state = colrow_get_states (sheet,
736 TRUE, r->start.col, r->end.col);
737 cr->row_state = colrow_get_states (sheet,
738 FALSE, r->start.row, r->end.row);
740 sheet_foreach_cell_in_range ( sheet, CELL_ITER_IGNORE_NONEXISTENT, r,
741 (CellIterFunc) cb_clipboard_prepend_cell,
742 cr);
743 objects = sheet_objects_get (sheet, r, G_TYPE_NONE);
744 g_slist_foreach (objects, (GFunc)cb_dup_objects, cr);
745 g_slist_free (objects);
747 cr->styles = sheet_style_get_range (sheet, r);
749 merged = gnm_sheet_merge_get_overlap (sheet, r);
750 for (ptr = merged ; ptr != NULL ; ptr = ptr->next) {
751 GnmRange *tmp = gnm_range_dup (ptr->data);
752 range_translate (tmp, sheet, -r->start.col, -r->start.row);
753 cr->merged = g_slist_prepend (cr->merged, tmp);
755 g_slist_free (merged);
757 return cr;
760 static void
761 cb_clipboard_copy_range_undo (GnmCellRegion *cr, GnmSheetRange *sr,
762 GOCmdContext *cc)
764 GnmPasteTarget pt;
766 clipboard_paste_region
767 (cr,
768 paste_target_init (&pt,
769 sr->sheet,
770 &sr->range,
771 PASTE_CONTENTS | PASTE_FORMATS |
772 PASTE_OBJECTS | PASTE_COMMENTS |
773 PASTE_COLUMN_WIDTHS | PASTE_ROW_HEIGHTS),
774 cc);
778 * clipboard_copy_range_undo:
779 * @sheet: #Sheet
780 * @r: #GnmRange
782 * Returns: (transfer full): A #GOUndo object that will restore the contents
783 * of the given range.
785 GOUndo *
786 clipboard_copy_range_undo (Sheet *sheet, GnmRange const *r)
788 GnmCellRegion *cr = clipboard_copy_range (sheet, r);
789 g_return_val_if_fail (cr != NULL, NULL);
790 return go_undo_binary_new (cr, gnm_sheet_range_new (sheet, r),
791 (GOUndoBinaryFunc)cb_clipboard_copy_range_undo,
792 (GFreeFunc)cellregion_unref,
793 (GFreeFunc)g_free);
797 * clipboard_copy_ranges_undo:
798 * @sheet: #Sheet
799 * @ranges: (element-type GnmRange) (transfer none): list of ranges
801 * Returns: (transfer full): A #GOUndo object that will restore the contents
802 * of the given range.
804 GOUndo *
805 clipboard_copy_ranges_undo (Sheet *sheet, GSList *ranges)
807 GSList *l;
808 GOUndo *undo = NULL;
810 for (l = ranges; l != NULL; l = l->next) {
811 GnmRange *r = l->data;
812 GOUndo *undo1 = clipboard_copy_range_undo (sheet, r);
813 undo = go_undo_combine (undo, undo1);
816 return undo;
821 * clipboard_copy_obj:
822 * @sheet: #Sheet
823 * @objects: (element-type SheetObject): #GSList
825 * Returns a cell region with copies of objects in list. Caller is responsible
826 * for cellregion_unref-ing the result.
828 GnmCellRegion *
829 clipboard_copy_obj (Sheet *sheet, GSList *objects)
831 SheetObjectAnchor tmp_anchor;
832 SheetObjectAnchor const *anchor;
833 GnmCellRegion *cr;
834 GnmRange *r;
835 GSList *ptr;
836 SheetObject *so;
837 double coords [4];
838 guint w, h;
840 g_return_val_if_fail (IS_SHEET (sheet), NULL);
841 g_return_val_if_fail (objects != NULL, NULL);
843 cr = gnm_cell_region_new (sheet);
844 for (ptr = objects ; ptr != NULL ; ptr = ptr->next)
845 if (NULL != (so = sheet_object_dup (ptr->data))) {
846 anchor = sheet_object_get_anchor (so);
848 #warning FIXME : This is only used in gnm_sog_write_image
849 /* NOTE #1 : It seems necessary to handle pasting an object that has been removed from
850 * the sheet after being added to the clipboard. it seems like we would need
851 * this sort of information for anything that implements SheetObjectImageableIface
853 sheet_object_anchor_to_pts (anchor, sheet, coords);
854 w = fabs (coords[2] - coords[0]) + 1.5;
855 h = fabs (coords[3] - coords[1]) + 1.5;
856 g_object_set_data (G_OBJECT (so), "pt-width-at-copy",
857 GUINT_TO_POINTER (w));
858 g_object_set_data (G_OBJECT (so), "pt-height-at-copy",
859 GUINT_TO_POINTER (h));
861 tmp_anchor = *anchor;
862 r = &tmp_anchor.cell_bound;
863 range_translate (r, sheet,
864 -MIN (r->start.col, r->end.col),
865 -MIN (r->start.row, r->end.row));
866 sheet_object_set_anchor (so, &tmp_anchor);
868 cr->objects = g_slist_prepend (cr->objects, so);
871 return cr;
874 GnmPasteTarget*
875 paste_target_init (GnmPasteTarget *pt, Sheet *sheet,
876 GnmRange const *r, GnmPasteFlags flags)
878 pt->sheet = sheet; // No ref
879 pt->range = *r;
880 pt->paste_flags = flags;
881 return pt;
885 * gnm_cell_region_new:
886 * @origin_sheet: optionally NULL.
888 * A convenience routine to create CellRegions and init the flags nicely.
890 GnmCellRegion *
891 gnm_cell_region_new (Sheet *origin_sheet)
893 GnmCellRegion *cr = g_new0 (GnmCellRegion, 1);
894 cr->origin_sheet = origin_sheet;
895 cr->date_conv = origin_sheet && origin_sheet->workbook
896 ? sheet_date_conv (origin_sheet)
897 : NULL;
898 cr->cols = cr->rows = -1;
899 cr->not_as_contents = FALSE;
900 cr->cell_content = NULL;
901 cr->col_state = NULL;
902 cr->row_state = NULL;
903 cr->styles = NULL;
904 cr->merged = NULL;
905 cr->objects = NULL;
906 cr->ref_count = 1;
908 return cr;
911 GnmCellRegion *
912 cellregion_ref (GnmCellRegion *cr)
914 g_return_val_if_fail (cr != NULL, NULL);
915 cr->ref_count++;
916 return cr;
919 void
920 cellregion_unref (GnmCellRegion *cr)
922 g_return_if_fail (cr != NULL);
924 if (cr->ref_count > 1) {
925 cr->ref_count--;
926 return;
929 if (NULL != cr->cell_content) {
930 g_hash_table_destroy (cr->cell_content);
931 cr->cell_content = NULL;
934 if (NULL != cr->col_state)
935 cr->col_state = colrow_state_list_destroy (cr->col_state);
936 if (NULL != cr->row_state)
937 cr->row_state = colrow_state_list_destroy (cr->row_state);
938 if (cr->styles != NULL) {
939 style_list_free (cr->styles);
940 cr->styles = NULL;
942 if (cr->merged != NULL) {
943 GSList *ptr;
944 for (ptr = cr->merged; ptr != NULL ; ptr = ptr->next)
945 g_free (ptr->data);
946 g_slist_free (cr->merged);
947 cr->merged = NULL;
949 if (cr->objects != NULL) {
950 GSList *ptr;
951 for (ptr = cr->objects; ptr != NULL ; ptr = ptr->next)
952 g_object_unref (ptr->data);
953 g_slist_free (cr->objects);
954 cr->objects = NULL;
957 g_free (cr);
960 GType
961 gnm_cell_region_get_type (void)
963 static GType t = 0;
965 if (t == 0) {
966 t = g_boxed_type_register_static ("GnmCellRegion",
967 (GBoxedCopyFunc)cellregion_ref,
968 (GBoxedFreeFunc)cellregion_unref);
970 return t;
973 static GnmCellCopy *
974 cellregion_get_content (GnmCellRegion const *cr, int col, int row)
976 if (cr->cell_content) {
977 GnmCellPos pos;
978 pos.col = col;
979 pos.row = row;
980 return g_hash_table_lookup (cr->cell_content, &pos);
981 } else
982 return NULL;
985 static void
986 cb_invalidate_cellcopy (GnmCellCopy *cc, gconstpointer ignore,
987 GnmExprRelocateInfo *rinfo)
989 GnmExprTop const *texpr;
990 if (NULL != cc->texpr) {
991 texpr = gnm_expr_top_relocate (cc->texpr, rinfo, FALSE);
992 if (NULL != texpr) {
993 gnm_expr_top_unref (cc->texpr);
994 cc->texpr = texpr;
1000 * cellregion_invalidate_sheet:
1001 * @cr: #GnmCellRegion
1002 * @sheet: #Sheet
1004 * Invalidate references from cell content, objects or style to @sheet.
1006 void
1007 cellregion_invalidate_sheet (GnmCellRegion *cr,
1008 Sheet *sheet)
1010 GSList *ptr;
1011 gboolean save_invalidated;
1012 GnmExprRelocateInfo rinfo;
1014 g_return_if_fail (cr != NULL);
1015 g_return_if_fail (IS_SHEET (sheet));
1017 save_invalidated = sheet->being_invalidated;
1018 sheet->being_invalidated = TRUE;
1020 rinfo.reloc_type = GNM_EXPR_RELOCATE_INVALIDATE_SHEET;
1021 if (NULL != cr->cell_content)
1022 g_hash_table_foreach (cr->cell_content,
1023 (GHFunc)cb_invalidate_cellcopy, &rinfo);
1024 sheet->being_invalidated = save_invalidated;
1026 for (ptr = cr->objects; ptr != NULL ; ptr = ptr->next)
1027 sheet_object_invalidate_sheet (ptr->data, sheet);
1029 if (cr->origin_sheet == sheet)
1030 cr->origin_sheet = NULL;
1033 static void
1034 cb_cellregion_extent (GnmCellCopy *cc, gconstpointer ignore, GnmRange *extent)
1036 if (extent->start.col >= 0) {
1037 if (extent->start.col > cc->offset.col)
1038 extent->start.col = cc->offset.col;
1039 else if (extent->end.col < cc->offset.col)
1040 extent->end.col = cc->offset.col;
1042 if (extent->start.row > cc->offset.row)
1043 extent->start.row = cc->offset.row;
1044 else if (extent->end.row < cc->offset.row)
1045 extent->end.row = cc->offset.row;
1046 } else /* first cell */
1047 extent->start = extent->end = cc->offset;
1051 * cellregion_extent:
1052 * @cr: #GnmCellRegion
1053 * @extent: #GnmRange
1055 * Find the min and max col/row with cell content
1057 static void
1058 cellregion_extent (GnmCellRegion const *cr, GnmRange *extent)
1060 if (NULL != cr->cell_content) {
1061 range_init (extent, -1, -1, -1, -1);
1062 g_hash_table_foreach (cr->cell_content,
1063 (GHFunc)cb_cellregion_extent, extent);
1064 } else
1065 range_init (extent, 0, 0, 0, 0);
1068 GString *
1069 cellregion_to_string (GnmCellRegion const *cr,
1070 gboolean only_visible,
1071 GODateConventions const *date_conv)
1073 GString *all, *line;
1074 GnmCellCopy const *cc;
1075 int col, row, next_col_check, next_row_check;
1076 GnmRange extent;
1077 ColRowStateList const *col_state = NULL, *row_state = NULL;
1078 ColRowRLEState const *rle;
1079 int ncells, i;
1080 GnmStyle const *style;
1081 GOFormat const *fmt;
1083 g_return_val_if_fail (cr != NULL, NULL);
1084 g_return_val_if_fail (cr->rows >= 0, NULL);
1085 g_return_val_if_fail (cr->cols >= 0, NULL);
1087 /* pre-allocate rough approximation of buffer */
1088 ncells = cr->cell_content ? g_hash_table_size (cr->cell_content) : 0;
1089 all = g_string_sized_new (20 * ncells + 1);
1090 line = g_string_new (NULL);
1092 cellregion_extent (cr, &extent);
1094 if (only_visible && NULL != (row_state = cr->row_state)) {
1095 next_row_check = i = 0;
1096 while ((i += ((ColRowRLEState *)(row_state->data))->length) <= extent.start.row) {
1097 if (NULL == (row_state = row_state->next)) {
1098 next_row_check = gnm_sheet_get_max_rows (cr->origin_sheet);
1099 break;
1101 next_row_check = i;
1103 } else
1104 next_row_check = gnm_sheet_get_max_rows (cr->origin_sheet);
1106 for (row = extent.start.row; row <= extent.end.row;) {
1107 if (row >= next_row_check) {
1108 rle = row_state->data;
1109 row_state = row_state->next;
1110 next_row_check += rle->length;
1111 if (!rle->state.visible) {
1112 row = next_row_check;
1113 continue;
1117 g_string_assign (line, "");
1119 if (only_visible && NULL != (col_state = cr->col_state)) {
1120 next_col_check = i = 0;
1121 while ((i += ((ColRowRLEState *)(col_state->data))->length) <= extent.start.col) {
1122 if (NULL == (col_state = col_state->next)) {
1123 next_col_check = gnm_sheet_get_max_cols (cr->origin_sheet);
1124 break;
1126 next_col_check = i;
1128 } else
1129 next_col_check = gnm_sheet_get_max_cols (cr->origin_sheet);
1131 for (col = extent.start.col; col <= extent.end.col;) {
1132 if (col == next_col_check) {
1133 rle = col_state->data;
1134 col_state = col_state->next;
1135 next_col_check += rle->length;
1136 if (!rle->state.visible) {
1137 col = next_col_check;
1138 continue;
1142 cc = cellregion_get_content (cr, col, row);
1143 if (cc) {
1144 style = style_list_get_style (cr->styles, col, row);
1145 fmt = gnm_style_get_format (style);
1147 if (go_format_is_general (fmt) &&
1148 VALUE_FMT (cc->val))
1149 fmt = VALUE_FMT (cc->val);
1151 format_value_gstring (line, fmt, cc->val,
1152 -1, date_conv);
1154 if (++col <= extent.end.col)
1155 g_string_append_c (line, '\t');
1157 g_string_append_len (all, line->str, line->len);
1158 if (++row <= extent.end.row)
1159 g_string_append_c (all, '\n');
1162 g_string_free (line, TRUE);
1163 return all;
1167 cellregion_cmd_size (GnmCellRegion const *cr)
1169 int res = 1;
1171 g_return_val_if_fail (cr != NULL, 1);
1173 res += g_slist_length (cr->styles);
1174 if (NULL != cr->cell_content)
1175 res += g_hash_table_size (cr->cell_content);
1176 return res;
1179 static void
1180 gnm_cell_copy_free (GnmCellCopy *cc)
1182 if (cc->texpr) {
1183 gnm_expr_top_unref (cc->texpr);
1184 cc->texpr = NULL;
1186 value_release (cc->val);
1187 cc->val = NULL;
1189 CHUNK_FREE (cell_copy_pool, cc);
1192 GnmCellCopy *
1193 gnm_cell_copy_new (GnmCellRegion *cr, int col_offset, int row_offset)
1195 GnmCellCopy *res = CHUNK_ALLOC (GnmCellCopy, cell_copy_pool);
1196 ((GnmCellPos *)(&res->offset))->col = col_offset;
1197 ((GnmCellPos *)(&res->offset))->row = row_offset;
1198 res->texpr = NULL;
1199 res->val = NULL;
1201 if (NULL == cr->cell_content)
1202 cr->cell_content = g_hash_table_new_full (
1203 (GHashFunc)&gnm_cellpos_hash,
1204 (GCompareFunc)&gnm_cellpos_equal,
1205 (GDestroyNotify) gnm_cell_copy_free,
1206 NULL);
1208 g_hash_table_insert (cr->cell_content, res, res);
1210 return res;
1214 * clipboard_init: (skip)
1216 void
1217 clipboard_init (void)
1219 #if USE_CELL_COPY_POOLS
1220 cell_copy_pool =
1221 go_mem_chunk_new ("cell copy pool",
1222 sizeof (GnmCellCopy),
1223 4 * 1024 - 128);
1224 #endif
1227 #if USE_CELL_COPY_POOLS
1228 static void
1229 cb_cell_copy_pool_leak (gpointer data, G_GNUC_UNUSED gpointer user)
1231 GnmCellCopy const *cc = data;
1232 g_printerr ("Leaking cell copy at %p.\n", (void *)cc);
1234 #endif
1237 * clipboard_shutdown: (skip)
1239 void
1240 clipboard_shutdown (void)
1242 #if USE_CELL_COPY_POOLS
1243 go_mem_chunk_foreach_leak (cell_copy_pool, cb_cell_copy_pool_leak, NULL);
1244 go_mem_chunk_destroy (cell_copy_pool, FALSE);
1245 cell_copy_pool = NULL;
1246 #endif