1 /*
2  * clipboard.c: A temporary store for contents from a worksheet
3  *
4  * Copyright (C) 2000-2008 Jody Goldberg   (jody@gnome.org)
5  *		 1999      Miguel de Icaza (miguel@gnu.org)
6  *
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.
11  *
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.
16  *
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
21  */
22 #include <gnumeric-config.h>
23 #include <gnumeric.h>
24 #include <clipboard.h>
25 
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 <style-conditions.h>
41 #include <stf-parse.h>
42 #include <gnm-format.h>
43 #include <sheet-object-cell-comment.h>
44 
45 #include <glib/gi18n-lib.h>
46 #include <locale.h>
47 #include <string.h>
48 #include <goffice/goffice.h>
49 
50 #ifndef USE_CELL_COPY_POOLS
51 #define USE_CELL_COPY_POOLS 1
52 #endif
53 
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
63 
64 /* creating a boxed type for GnmCellCopy (needed by introspection) */
65 static gpointer
pointer_dup(gpointer * cc)66 pointer_dup (gpointer *cc)
67 {
68 	return cc;
69 }
70 
71 GType
gnm_cell_copy_get_type(void)72 gnm_cell_copy_get_type (void)
73 {
74 	static GType t = 0;
75 
76 	if (t == 0) {
77 		t = g_boxed_type_register_static ("GnmCellCopy",
78 			 (GBoxedCopyFunc)pointer_dup,
79 			 (GBoxedFreeFunc)pointer_dup);
80 	}
81 	return t;
82 }
83 
84 /* creating a boxed type for GnmPasteTarget (needed by introspection) */
85 
86 static GnmPasteTarget *
gnm_paste_target_copy(GnmPasteTarget * pt)87 gnm_paste_target_copy (GnmPasteTarget *pt)
88 {
89 	return g_memdup (pt, sizeof (*pt));
90 }
91 
92 GType
gnm_paste_target_get_type(void)93 gnm_paste_target_get_type (void)
94 {
95 	static GType t = 0;
96 
97 	if (t == 0) {
98 		t = g_boxed_type_register_static ("GnmPasteTarget",
99 			 (GBoxedCopyFunc)gnm_paste_target_copy,
100 			 (GBoxedFreeFunc)g_free);
101 	}
102 	return t;
103 }
104 
105 GnmPasteTarget *
gnm_paste_target_new(Sheet * sheet,GnmRange * r,GnmPasteFlags flags)106 gnm_paste_target_new (Sheet *sheet, GnmRange *r, GnmPasteFlags flags)
107 {
108 	GnmPasteTarget *res = g_new (GnmPasteTarget, 1);
109 	paste_target_init (res, sheet, r, flags);
110 	return res;
111 }
112 
113 
114 static gboolean
cell_has_expr_or_number_or_blank(GnmCell const * cell)115 cell_has_expr_or_number_or_blank (GnmCell const * cell)
116 {
117 	return (gnm_cell_is_empty (cell) ||
118 		(cell != NULL && gnm_cell_is_number (cell)) ||
119 		(cell != NULL && gnm_cell_has_expr (cell)));
120 }
121 
122 static GnmExpr const *
contents_as_expr(GnmExprTop const * texpr,GnmValue const * val)123 contents_as_expr (GnmExprTop const *texpr, GnmValue const *val)
124 {
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;
132 }
133 
134 static GnmExprOp
paste_op_to_expr_op(int paste_flags)135 paste_op_to_expr_op (int paste_flags)
136 {
137 	g_return_val_if_fail (paste_flags & PASTE_OPER_MASK, 0);
138 
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;
147 
148 	return 0;
149 }
150 
151 static void
paste_cell_with_operation(Sheet * dst_sheet,int target_col,int target_row,GnmExprRelocateInfo const * rinfo,GnmCellCopy const * src,int paste_flags)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)
157 {
158 	GnmCell *dst;
159 	GnmExprOp op;
160 
161 	if (src->texpr == NULL &&
162 	    !VALUE_IS_EMPTY (src->val) &&
163 	    !VALUE_IS_NUMBER (src->val))
164 		return;
165 
166 	dst = sheet_cell_fetch (dst_sheet, target_col, target_row);
167 	if (!cell_has_expr_or_number_or_blank (dst))
168 		return;
169 
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)),
189 			op,
190 			gnm_expr_new_constant (value_dup (src->val)));
191 		GnmExprTop const *texpr = gnm_expr_top_new (expr);
192 
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);
199 	}
200 }
201 
202 /* NOTE : Make sure to set up any merged regions in the target range BEFORE
203  * this is called.
204  */
205 static void
paste_link(GnmPasteTarget const * pt,int top,int left,GnmCellRegion const * cr)206 paste_link (GnmPasteTarget const *pt, int top, int left,
207 	    GnmCellRegion const *cr)
208 {
209 	GnmCellPos pos;
210 	GnmCellRef source_cell_ref;
211 	int x, y;
212 
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;
217 
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);
231 
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);
240 		}
241 	}
242 }
243 
244 struct paste_cell_data {
245 	GnmPasteTarget const *pt;
246 	GnmCellRegion const  *cr;
247 	GnmCellPos	top_left;
248 	GnmExprRelocateInfo rinfo;
249 	gboolean translate_dates;
250 };
251 
252 /**
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.
258  *
259  * Pastes a cell in the spreadsheet.
260  */
261 static void
paste_cell(int target_col,int target_row,GnmCellCopy const * src,const struct paste_cell_data * dat)262 paste_cell (int target_col, int target_row,
263 	    GnmCellCopy const *src,
264 	    const struct paste_cell_data *dat)
265 {
266 	Sheet *dst_sheet = dat->pt->sheet;
267 	int paste_flags = dat->pt->paste_flags;
268 
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;
284 				}
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));
288 			}
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;
296 
297 			if (dat->translate_dates && oldval && VALUE_IS_FLOAT (oldval)) {
298 				GOFormat const *fmt = VALUE_FMT (oldval)
299 					? VALUE_FMT (oldval)
300 					: gnm_cell_get_format (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));
308 				}
309 			}
310 
311 			if (!newval)
312 				newval = value_dup (src->val);
313 			gnm_cell_set_value (dst, newval);
314 		}
315 	}
316 }
317 
318 static void
paste_object(GnmPasteTarget const * pt,SheetObject const * src,int left,int top)319 paste_object (GnmPasteTarget const *pt, SheetObject const *src, int left, int top)
320 {
321 	SheetObject *dst;
322 	SheetObjectAnchor tmp;
323 
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;
333 
334 	if (NULL == (dst = sheet_object_dup (src)))
335 		return;
336 
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);
342 	}
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);
347 }
348 
349 static void
cb_paste_cell(GnmCellCopy const * src,gconstpointer ignore,struct paste_cell_data * dat)350 cb_paste_cell (GnmCellCopy const *src, gconstpointer ignore,
351 	       struct paste_cell_data *dat)
352 {
353 	int target_col = dat->top_left.col;
354 	int target_row = dat->top_left.row;
355 
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;
368 	}
369 
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;
377 	}
378 
379 	paste_cell (target_col, target_row, src, dat);
380 }
381 
382 static gboolean
range_flip_h(GnmRange * range,Sheet const * sheet,int const * data)383 range_flip_h (GnmRange *range, Sheet const *sheet, int const *data)
384 {
385 	int t;
386 
387 	g_return_val_if_fail (range != NULL, TRUE);
388 
389 	t = *data - range->end.col;
390 	range->end.col = *data -  range->start.col;
391 	range->start.col = t;
392 
393 	return FALSE;
394 }
395 static gboolean
range_flip_v(GnmRange * range,Sheet const * sheet,int const * data)396 range_flip_v (GnmRange *range, Sheet const *sheet, int const *data)
397 {
398 	int t;
399 
400 	g_return_val_if_fail (range != NULL, TRUE);
401 
402 	t = *data - range->end.row;
403 	range->end.row = *data -  range->start.row;
404 	range->start.row = t;
405 
406 	return FALSE;
407 }
408 
409 /**
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.
414  *
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.
419  *
420  * Returns: %TRUE if there was a problem.
421  **/
422 gboolean
clipboard_paste_region(GnmCellRegion const * cr,GnmPasteTarget const * pt,GOCmdContext * cc)423 clipboard_paste_region (GnmCellRegion const *cr,
424 			GnmPasteTarget const *pt,
425 			GOCmdContext *cc)
426 {
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;
436 
437 	g_return_val_if_fail (pt != NULL, TRUE);
438 	g_return_val_if_fail (cr != NULL, TRUE);
439 
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;
450 	}
451 
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;
457 
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;
471 		}
472 	/* Apparently links do not supercede merges */
473 	} else if (pt->paste_flags & PASTE_LINK)
474 		adjust_merges = FALSE;
475 
476 	has_contents = pt->paste_flags & (PASTE_CONTENTS|PASTE_AS_VALUES|PASTE_LINK);
477 
478 	if (pt->paste_flags & PASTE_TRANSPOSE) {
479 		int tmp = src_cols;
480 		src_cols = src_rows;
481 		src_rows = tmp;
482 	}
483 
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;
491 	}
492 
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;
504 	}
505 
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;
516 	}
517 
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;
526 	}
527 
528 	clearFlags = 0;
529 	/* clear the region where we will paste */
530 	if (has_contents)
531 		clearFlags = CLEAR_VALUES | CLEAR_NORESPAN;
532 
533 	if (pt->paste_flags & PASTE_COMMENTS)
534 		clearFlags |= CLEAR_COMMENTS;
535 
536 	/* No need to clear the formats.  We will paste over top of these. */
537 	/* if (pt->paste_flags & PASTE_FORMATS) clearFlags |= CLEAR_FORMATS; */
538 
539 	if (pt->paste_flags & (PASTE_OPER_MASK | PASTE_SKIP_BLANKS))
540 		clearFlags = 0;
541 
542 	/* remove merged regions even for operations, or blanks */
543 	if (has_contents && adjust_merges)
544 		clearFlags |= CLEAR_MERGES;
545 
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);
554 	}
555 
556 	dat.translate_dates = cr->date_conv &&
557 		!go_date_conv_equal (cr->date_conv, sheet_date_conv (pt->sheet));
558 
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;
563 
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;
578 			}
579 
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);
603 			}
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;
611 					}
612 					if (!range_translate (&tmp, pt->sheet, left, top))
613 						gnm_sheet_merge_add (pt->sheet, &tmp, TRUE, cc);
614 				}
615 			}
616 
617 			if (has_contents && (pt->paste_flags & PASTE_LINK)) {
618 				paste_link (pt, top, left, cr);
619 				continue;
620 			}
621 
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);
627 			}
628 
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);
632 		}
633 
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);
646 		}
647 	}
648 
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);
660 		}
661 	}
662 
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);
669 
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);
673 	}
674 
675 	return FALSE;
676 }
677 
678 static GnmValue *
cb_clipboard_prepend_cell(GnmCellIter const * iter,GnmCellRegion * cr)679 cb_clipboard_prepend_cell (GnmCellIter const *iter, GnmCellRegion *cr)
680 {
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);
686 
687 	if (gnm_cell_has_expr (iter->cell)) {
688 		gnm_expr_top_ref (copy->texpr = iter->cell->base.texpr);
689 
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;
700 
701 	return NULL;
702 }
703 
704 static void
cb_dup_objects(SheetObject const * src,GnmCellRegion * cr)705 cb_dup_objects (SheetObject const *src, GnmCellRegion *cr)
706 {
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);
714 	}
715 }
716 
717 /**
718  * clipboard_copy_range:
719  *
720  * Entry point to the clipboard copy code
721  */
722 GnmCellRegion *
clipboard_copy_range(Sheet * sheet,GnmRange const * r)723 clipboard_copy_range (Sheet *sheet, GnmRange const *r)
724 {
725 	GnmCellRegion *cr;
726 	GSList *merged, *ptr;
727 	GSList *objects;
728 
729 	g_return_val_if_fail (IS_SHEET (sheet), NULL);
730 	g_return_val_if_fail (range_is_sane (r), NULL);
731 
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);
740 
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);
747 
748 	cr->styles = sheet_style_get_range (sheet, r);
749 
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);
755 	}
756 	g_slist_free (merged);
757 
758 	return cr;
759 }
760 
761 static void
cb_clipboard_copy_range_undo(GnmCellRegion * cr,GnmSheetRange * sr,GOCmdContext * cc)762 cb_clipboard_copy_range_undo (GnmCellRegion *cr, GnmSheetRange *sr,
763 			      GOCmdContext *cc)
764 {
765 	GnmPasteTarget pt;
766 
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);
776 }
777 
778 /**
779  * clipboard_copy_range_undo:
780  * @sheet: #Sheet
781  * @r: #GnmRange
782  *
783  * Returns: (transfer full): A #GOUndo object that will restore the contents
784  * of the given range.
785  **/
786 GOUndo *
clipboard_copy_range_undo(Sheet * sheet,GnmRange const * r)787 clipboard_copy_range_undo (Sheet *sheet, GnmRange const *r)
788 {
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);
795 }
796 
797 /**
798  * clipboard_copy_ranges_undo:
799  * @sheet: #Sheet
800  * @ranges: (element-type GnmRange) (transfer none): list of ranges
801  *
802  * Returns: (transfer full): A #GOUndo object that will restore the contents
803  * of the given range.
804  **/
805 GOUndo *
clipboard_copy_ranges_undo(Sheet * sheet,GSList * ranges)806 clipboard_copy_ranges_undo (Sheet *sheet, GSList *ranges)
807 {
808 	GSList *l;
809 	GOUndo *undo = NULL;
810 
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);
815 	}
816 
817 	return undo;
818 }
819 
820 
821 /**
822  * clipboard_copy_obj:
823  * @sheet: #Sheet
824  * @objects: (element-type SheetObject): #GSList
825  *
826  * Returns a cell region with copies of objects in list.  Caller is responsible
827  *	for cellregion_unref-ing the result.
828  **/
829 GnmCellRegion *
clipboard_copy_obj(Sheet * sheet,GSList * objects)830 clipboard_copy_obj (Sheet *sheet, GSList *objects)
831 {
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;
840 
841 	g_return_val_if_fail (IS_SHEET (sheet), NULL);
842 	g_return_val_if_fail (objects != NULL, NULL);
843 
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);
848 
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
853  **/
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));
861 
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);
868 
869 			cr->objects = g_slist_prepend (cr->objects, so);
870 		}
871 
872 	return cr;
873 }
874 
875 GnmPasteTarget*
paste_target_init(GnmPasteTarget * pt,Sheet * sheet,GnmRange const * r,GnmPasteFlags flags)876 paste_target_init (GnmPasteTarget *pt, Sheet *sheet,
877 		   GnmRange const *r, GnmPasteFlags flags)
878 {
879 	pt->sheet = sheet; // No ref
880 	pt->range = *r;
881 	pt->paste_flags = flags;
882 	return pt;
883 }
884 
885 /**
886  * gnm_cell_region_new:
887  * @origin_sheet: optionally NULL.
888  *
889  * A convenience routine to create CellRegions and init the flags nicely.
890  */
891 GnmCellRegion *
gnm_cell_region_new(Sheet * origin_sheet)892 gnm_cell_region_new (Sheet *origin_sheet)
893 {
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;
908 
909 	return cr;
910 }
911 
912 GnmCellRegion *
cellregion_ref(GnmCellRegion * cr)913 cellregion_ref (GnmCellRegion *cr)
914 {
915 	g_return_val_if_fail (cr != NULL, NULL);
916 	cr->ref_count++;
917 	return cr;
918 }
919 
920 void
cellregion_unref(GnmCellRegion * cr)921 cellregion_unref (GnmCellRegion *cr)
922 {
923 	g_return_if_fail (cr != NULL);
924 
925 	if (cr->ref_count > 1) {
926 		cr->ref_count--;
927 		return;
928 	}
929 
930 	if (NULL != cr->cell_content) {
931 		g_hash_table_destroy (cr->cell_content);
932 		cr->cell_content = NULL;
933 	}
934 
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;
942 	}
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;
949 	}
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;
956 	}
957 
958 	g_free (cr);
959 }
960 
961 GType
gnm_cell_region_get_type(void)962 gnm_cell_region_get_type (void)
963 {
964 	static GType t = 0;
965 
966 	if (t == 0) {
967 		t = g_boxed_type_register_static ("GnmCellRegion",
968 			 (GBoxedCopyFunc)cellregion_ref,
969 			 (GBoxedFreeFunc)cellregion_unref);
970 	}
971 	return t;
972 }
973 
974 static GnmCellCopy *
cellregion_get_content(GnmCellRegion const * cr,int col,int row)975 cellregion_get_content (GnmCellRegion const *cr, int col, int row)
976 {
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;
984 }
985 
986 static void
cb_invalidate_cellcopy(GnmCellCopy * cc,gconstpointer ignore,GnmExprRelocateInfo * rinfo)987 cb_invalidate_cellcopy (GnmCellCopy *cc, gconstpointer ignore,
988 			GnmExprRelocateInfo *rinfo)
989 {
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;
996 		}
997 	}
998 }
999 
1000 /**
1001  * cellregion_invalidate_sheet:
1002  * @cr: #GnmCellRegion
1003  * @sheet: #Sheet
1004  *
1005  * Invalidate references from cell content, objects or style to @sheet.
1006  **/
1007 void
cellregion_invalidate_sheet(GnmCellRegion * cr,Sheet * sheet)1008 cellregion_invalidate_sheet (GnmCellRegion *cr,
1009 			     Sheet *sheet)
1010 {
1011 	GSList *ptr;
1012 	gboolean save_invalidated;
1013 	GnmExprRelocateInfo rinfo;
1014 	GnmStyleList *l;
1015 
1016 	g_return_if_fail (cr != NULL);
1017 	g_return_if_fail (IS_SHEET (sheet));
1018 
1019 	save_invalidated = sheet->being_invalidated;
1020 	sheet->being_invalidated = TRUE;
1021 
1022 	rinfo.reloc_type = GNM_EXPR_RELOCATE_INVALIDATE_SHEET;
1023 	if (NULL != cr->cell_content)
1024 		g_hash_table_foreach (cr->cell_content,
1025 			(GHFunc)cb_invalidate_cellcopy, &rinfo);
1026 	sheet->being_invalidated = save_invalidated;
1027 
1028 	// Remove conditional formats from styles.  That's brutal, but
1029 	// they reference the sheet.  See #406.
1030 	for (l = cr->styles; l; l = l->next) {
1031 		GnmStyleRegion *sr = l->data;
1032 		GnmRange const *r = &sr->range;
1033 		GnmStyle const *style = sr->style;
1034 		GnmStyleConditions *conds = gnm_style_is_element_set (style, MSTYLE_CONDITIONS)
1035 			? gnm_style_get_conditions (style)
1036 			: NULL;
1037 
1038 		if (conds &&
1039 		    gnm_style_conditions_get_sheet (conds) == sheet) {
1040 			GnmStyle *style2;
1041 			GnmStyleRegion *sr2;
1042 
1043 			style2 = gnm_style_dup (style);
1044 			gnm_style_set_conditions (style2, NULL);
1045 			sr2 = gnm_style_region_new (r, style2);
1046 			gnm_style_unref (style2);
1047 
1048 			gnm_style_region_free (sr);
1049 			l->data = sr2;
1050 		}
1051 	}
1052 
1053 	for (ptr = cr->objects; ptr != NULL ; ptr = ptr->next)
1054 		sheet_object_invalidate_sheet (ptr->data, sheet);
1055 
1056 	if (cr->origin_sheet == sheet)
1057 		cr->origin_sheet = NULL;
1058 }
1059 
1060 static void
cb_cellregion_extent(GnmCellCopy * cc,gconstpointer ignore,GnmRange * extent)1061 cb_cellregion_extent (GnmCellCopy *cc, gconstpointer ignore, GnmRange *extent)
1062 {
1063 	if (extent->start.col >= 0) {
1064 		if (extent->start.col > cc->offset.col)
1065 			extent->start.col = cc->offset.col;
1066 		else if (extent->end.col < cc->offset.col)
1067 			extent->end.col = cc->offset.col;
1068 
1069 		if (extent->start.row > cc->offset.row)
1070 			extent->start.row = cc->offset.row;
1071 		else if (extent->end.row < cc->offset.row)
1072 			extent->end.row = cc->offset.row;
1073 	} else /* first cell */
1074 		extent->start = extent->end = cc->offset;
1075 }
1076 
1077 /**
1078  * cellregion_extent:
1079  * @cr: #GnmCellRegion
1080  * @extent: #GnmRange
1081  *
1082  * Find the min and max col/row with cell content
1083  **/
1084 static void
cellregion_extent(GnmCellRegion const * cr,GnmRange * extent)1085 cellregion_extent (GnmCellRegion const *cr, GnmRange *extent)
1086 {
1087 	if (NULL != cr->cell_content) {
1088 		range_init (extent, -1, -1, -1, -1);
1089 		g_hash_table_foreach (cr->cell_content,
1090 			(GHFunc)cb_cellregion_extent, extent);
1091 	} else
1092 		range_init (extent, 0, 0, 0, 0);
1093 }
1094 
1095 GString *
cellregion_to_string(GnmCellRegion const * cr,gboolean only_visible,GODateConventions const * date_conv)1096 cellregion_to_string (GnmCellRegion const *cr,
1097 		      gboolean only_visible,
1098 		      GODateConventions const *date_conv)
1099 {
1100 	GString *all, *line;
1101 	GnmCellCopy const *cc;
1102 	int col, row, next_col_check, next_row_check;
1103 	GnmRange extent;
1104 	ColRowStateList	const *col_state = NULL, *row_state = NULL;
1105 	ColRowRLEState const *rle;
1106 	int ncells, i;
1107 	GnmStyle const *style;
1108 	GOFormat const *fmt;
1109 
1110 	g_return_val_if_fail (cr != NULL, NULL);
1111 	g_return_val_if_fail (cr->rows >= 0, NULL);
1112 	g_return_val_if_fail (cr->cols >= 0, NULL);
1113 
1114 	/* pre-allocate rough approximation of buffer */
1115 	ncells = cr->cell_content ? g_hash_table_size (cr->cell_content) : 0;
1116 	all = g_string_sized_new (20 * ncells + 1);
1117 	line = g_string_new (NULL);
1118 
1119 	cellregion_extent (cr, &extent);
1120 
1121 	if (only_visible && NULL != (row_state = cr->row_state)) {
1122 		next_row_check = i = 0;
1123 		while ((i += ((ColRowRLEState *)(row_state->data))->length) <= extent.start.row) {
1124 			if (NULL == (row_state = row_state->next)) {
1125 				next_row_check = gnm_sheet_get_max_rows (cr->origin_sheet);
1126 				break;
1127 			}
1128 			next_row_check = i;
1129 		}
1130 	} else
1131 		next_row_check = gnm_sheet_get_max_rows (cr->origin_sheet);
1132 
1133 	for (row = extent.start.row; row <= extent.end.row;) {
1134 		if (row >= next_row_check) {
1135 			rle = row_state->data;
1136 			row_state = row_state->next;
1137 			next_row_check += rle->length;
1138 			if (!rle->state.visible) {
1139 				row = next_row_check;
1140 				continue;
1141 			}
1142 		}
1143 
1144 		g_string_assign (line, "");
1145 
1146 		if (only_visible && NULL != (col_state = cr->col_state)) {
1147 			next_col_check = i = 0;
1148 			while ((i += ((ColRowRLEState *)(col_state->data))->length) <= extent.start.col) {
1149 				if (NULL == (col_state = col_state->next)) {
1150 					next_col_check = gnm_sheet_get_max_cols (cr->origin_sheet);
1151 					break;
1152 				}
1153 				next_col_check = i;
1154 			}
1155 		} else
1156 			next_col_check = gnm_sheet_get_max_cols (cr->origin_sheet);
1157 
1158 		for (col = extent.start.col; col <= extent.end.col;) {
1159 			if (col == next_col_check) {
1160 				rle = col_state->data;
1161 				col_state = col_state->next;
1162 				next_col_check += rle->length;
1163 				if (!rle->state.visible) {
1164 					col = next_col_check;
1165 					continue;
1166 				}
1167 			}
1168 
1169 			cc = cellregion_get_content (cr, col, row);
1170 			if (cc) {
1171 				style = style_list_get_style (cr->styles, col, row);
1172 				fmt = gnm_style_get_format (style);
1173 
1174 				if (go_format_is_general (fmt) &&
1175 				    VALUE_FMT (cc->val))
1176 					fmt = VALUE_FMT (cc->val);
1177 
1178 				format_value_gstring (line, fmt, cc->val,
1179 						      -1, date_conv);
1180 			}
1181 			if (++col <= extent.end.col)
1182 				g_string_append_c (line, '\t');
1183 		}
1184 		g_string_append_len (all, line->str, line->len);
1185 		if (++row <= extent.end.row)
1186 			g_string_append_c (all, '\n');
1187 	}
1188 
1189 	g_string_free (line, TRUE);
1190 	return all;
1191 }
1192 
1193 int
cellregion_cmd_size(GnmCellRegion const * cr)1194 cellregion_cmd_size (GnmCellRegion const *cr)
1195 {
1196 	int res = 1;
1197 
1198 	g_return_val_if_fail (cr != NULL, 1);
1199 
1200 	res += g_slist_length (cr->styles);
1201 	if (NULL != cr->cell_content)
1202 		res += g_hash_table_size (cr->cell_content);
1203 	return res;
1204 }
1205 
1206 static void
gnm_cell_copy_free(GnmCellCopy * cc)1207 gnm_cell_copy_free (GnmCellCopy *cc)
1208 {
1209 	if (cc->texpr) {
1210 		gnm_expr_top_unref (cc->texpr);
1211 		cc->texpr = NULL;
1212 	}
1213 	value_release (cc->val);
1214 	cc->val = NULL;
1215 
1216 	CHUNK_FREE (cell_copy_pool, cc);
1217 }
1218 
1219 GnmCellCopy *
gnm_cell_copy_new(GnmCellRegion * cr,int col_offset,int row_offset)1220 gnm_cell_copy_new (GnmCellRegion *cr, int col_offset, int row_offset)
1221 {
1222 	GnmCellCopy *res = CHUNK_ALLOC (GnmCellCopy, cell_copy_pool);
1223 	((GnmCellPos *)(&res->offset))->col = col_offset;
1224 	((GnmCellPos *)(&res->offset))->row = row_offset;
1225 	res->texpr = NULL;
1226 	res->val = NULL;
1227 
1228 	if (NULL == cr->cell_content)
1229 		cr->cell_content = g_hash_table_new_full (
1230 			(GHashFunc)&gnm_cellpos_hash,
1231 			(GCompareFunc)&gnm_cellpos_equal,
1232 			(GDestroyNotify) gnm_cell_copy_free,
1233 			NULL);
1234 
1235 	g_hash_table_insert (cr->cell_content, res, res);
1236 
1237 	return res;
1238 }
1239 
1240 /**
1241  * clipboard_init: (skip)
1242  */
1243 void
clipboard_init(void)1244 clipboard_init (void)
1245 {
1246 #if USE_CELL_COPY_POOLS
1247 	cell_copy_pool =
1248 		go_mem_chunk_new ("cell copy pool",
1249 				  sizeof (GnmCellCopy),
1250 				  4 * 1024 - 128);
1251 #endif
1252 }
1253 
1254 #if USE_CELL_COPY_POOLS
1255 static void
cb_cell_copy_pool_leak(gpointer data,G_GNUC_UNUSED gpointer user)1256 cb_cell_copy_pool_leak (gpointer data, G_GNUC_UNUSED gpointer user)
1257 {
1258 	GnmCellCopy const *cc = data;
1259 	g_printerr ("Leaking cell copy at %p.\n", (void *)cc);
1260 }
1261 #endif
1262 
1263 /**
1264  * clipboard_shutdown: (skip)
1265  */
1266 void
clipboard_shutdown(void)1267 clipboard_shutdown (void)
1268 {
1269 #if USE_CELL_COPY_POOLS
1270 	go_mem_chunk_foreach_leak (cell_copy_pool, cb_cell_copy_pool_leak, NULL);
1271 	go_mem_chunk_destroy (cell_copy_pool, FALSE);
1272 	cell_copy_pool = NULL;
1273 #endif
1274 }
1275