1 /*
2  * ranges.c: various functions for common operations on cell ranges.
3  *
4  * Author:
5  *   Jody Goldberg   (jody@gnome.org)
6  *   Miguel de Icaza (miguel@gnu.org).
7  *   Michael Meeks   (mmeeks@gnu.org).
8  * Copyright (C) 2007-2009 Morten Welinder (terra@gnome.org)
9  */
10 #include <gnumeric-config.h>
11 #include <gnumeric.h>
12 #include <ranges.h>
13 
14 #include <commands.h>
15 #include <numbers.h>
16 #include <expr.h>
17 #include <expr-impl.h>
18 #include <expr-name.h>
19 #include <sheet.h>
20 #include <sheet-style.h>
21 #include <parse-util.h>
22 #include <value.h>
23 #include <cell.h>
24 #include <style.h>
25 #include <workbook.h>
26 #include <gnumeric-conf.h>
27 
28 #include <stdlib.h>
29 #include <glib.h>
30 #include <glib/gi18n-lib.h>
31 #include <string.h>
32 
33 #undef RANGE_DEBUG
34 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
35 
36 /**
37  * range_init_full_sheet:
38  * @r: #GnmRange
39  * @sheet: #Sheet
40  *
41  * Updates @r to fill @sheet in its entirety.
42  *
43  * Returns: (type void): @r
44  */
45 GnmRange *
range_init_full_sheet(GnmRange * r,Sheet const * sheet)46 range_init_full_sheet (GnmRange *r, Sheet const *sheet)
47 {
48 	r->start.col = 0;
49 	r->start.row = 0;
50 	r->end.col = gnm_sheet_get_last_col (sheet);
51 	r->end.row = gnm_sheet_get_last_row (sheet);
52 	return r;
53 }
54 
55 /**
56  * range_init_cols:
57  * @r: #GnmRange
58  * @sheet: #Sheet
59  * @start_col: Starting column
60  * @end_col: Ending column
61  *
62  * Updates @r to span full columns @start_col through @end_col completely.
63  *
64  * Returns: (type void): @r
65  */
66 GnmRange *
range_init_cols(GnmRange * r,Sheet const * sheet,int start_col,int end_col)67 range_init_cols (GnmRange *r, Sheet const *sheet, int start_col, int end_col)
68 {
69 	r->start.col = start_col;
70 	r->start.row = 0;
71 	r->end.col = end_col;
72 	r->end.row = gnm_sheet_get_last_row (sheet);
73 	return r;
74 }
75 
76 /**
77  * range_init_rows:
78  * @r: #GnmRange
79  * @sheet: #Sheet
80  * @start_row: Starting row
81  * @end_row: Ending row
82  *
83  * Updates @r to span full rows @start_row through @end_row completely.
84  *
85  * Returns: (type void): @r
86  */
87 GnmRange *
range_init_rows(GnmRange * r,Sheet const * sheet,int start_row,int end_row)88 range_init_rows (GnmRange *r, Sheet const *sheet, int start_row, int end_row)
89 {
90 	r->start.col = 0;
91 	r->start.row = start_row;
92 	r->end.col = gnm_sheet_get_last_col (sheet);
93 	r->end.row = end_row;
94 	return r;
95 }
96 
97 /**
98  * range_init_invalid: (skip)
99  * @r: #GnmRange
100  *
101  * Updates @r to a meaningless range
102  *
103  * Returns: (type void): @r
104  */
105 GnmRange *
range_init_invalid(GnmRange * r)106 range_init_invalid (GnmRange *r)
107 {
108 	r->start.col = -1;
109 	r->start.row = -1;
110 	r->end.col = -2;
111 	r->end.row = -2;
112 	return r;
113 }
114 
115 /**
116  * range_init_rangeref:
117  * @r: #GnmRange
118  * @rr: #GnmRangeRef
119  *
120  * Updates @r to be the same as the range part of @rr.
121  *
122  * Returns: (type void): @r
123  */
124 GnmRange *
range_init_rangeref(GnmRange * range,GnmRangeRef const * rr)125 range_init_rangeref (GnmRange *range, GnmRangeRef const *rr)
126 {
127 	g_return_val_if_fail (range != NULL && rr != NULL, NULL);
128 
129 	range->start.col = rr->a.col;
130 	range->start.row = rr->a.row;
131 	range->end.col   = rr->b.col;
132 	range->end.row   = rr->b.row;
133 	return range;
134 }
135 
136 
137 /**
138  * range_init_value:
139  * @r: A #GnmRange to change
140  * @v: A #GnmValue containing a cell range
141  *
142  * Updates @r to be the same as the cell range of @v.
143  *
144  * Returns: (type void): @r
145  */
146 GnmRange *
range_init_value(GnmRange * range,GnmValue const * v)147 range_init_value (GnmRange *range, GnmValue const *v)
148 {
149 	g_return_val_if_fail (range != NULL, NULL);
150 	g_return_val_if_fail (v != NULL && VALUE_IS_CELLRANGE (v), NULL);
151 
152 	return range_init_rangeref (range, &v->v_range.cell);
153 }
154 
155 /**
156  * range_init_cellpos:
157  * @r: A #GnmRange to change
158  * @pos: A #GnmCellPos
159  *
160  * Updates @r to be the singleton range of @pos
161  *
162  * Returns: (type void): @r
163  */
164 GnmRange *
range_init_cellpos(GnmRange * r,GnmCellPos const * pos)165 range_init_cellpos (GnmRange *r, GnmCellPos const *pos)
166 {
167 	r->start = *pos;
168 	r->end = *pos;
169 
170 	return r;
171 }
172 
173 /**
174  * range_init_cellpos_size:
175  * @r: A #GnmRange to change
176  * @start: A #GnmCellPos for the upper left corner of the desired range
177  * @cols: number of columns
178  * @rows: number of rows
179  *
180  * Updates @r to start at @start and spanning @cols columns and @rows rows.
181  *
182  * Returns: (type void): @r
183  */
184 GnmRange *
range_init_cellpos_size(GnmRange * r,GnmCellPos const * start,int cols,int rows)185 range_init_cellpos_size (GnmRange *r,
186 			 GnmCellPos const *start, int cols, int rows)
187 {
188 	r->start = *start;
189 	r->end.col = start->col + cols - 1;
190 	r->end.row = start->row + rows - 1;
191 
192 	return r;
193 }
194 
195 /**
196  * range_init:
197  * @r: A #GnmRange to change
198  * @start_col: Column
199  * @start_row: Row
200  * @end_col: Column
201  * @end_row: Row
202  *
203  * Updates @r to start at (@start_col,@start_row) and end
204  * at (@end_col,@end_row).
205  *
206  * Returns: (type void): @r
207  */
208 GnmRange *
range_init(GnmRange * r,int start_col,int start_row,int end_col,int end_row)209 range_init (GnmRange *r, int start_col, int start_row,
210 	    int end_col, int end_row)
211 {
212 	g_return_val_if_fail (r != NULL, r);
213 
214 	r->start.col = start_col;
215 	r->start.row = start_row;
216 	r->end.col   = end_col;
217 	r->end.row   = end_row;
218 
219 	return r;
220 }
221 
222 /**
223  * range_parse:
224  * @r: #GnmRange
225  * @text: text to parse
226  * @ss: #GnmSheetSize describing the size of the sheet in which @r lives.
227  *
228  * Parse a simple range (no abs/rel refs, no sheet refs)
229  * Store the result in @r.
230  *
231  * Returns: %TRUE on success.
232  **/
233 gboolean
range_parse(GnmRange * r,char const * text,GnmSheetSize const * ss)234 range_parse (GnmRange *r, char const *text, GnmSheetSize const *ss)
235 {
236 	text = cellpos_parse (text, ss, &r->start, FALSE);
237 	if (!text)
238 		return FALSE;
239 
240 	if (*text == '\0') {
241 		r->end = r->start;
242 		return TRUE;
243 	}
244 
245 	if (*text != ':')
246 		return FALSE;
247 
248 	text = cellpos_parse (text + 1, ss, &r->end, TRUE);
249 	return text != NULL;
250 }
251 
252 /**
253  * range_list_destroy:
254  * @ranges: (element-type GnmValue) (transfer full): a list of value ranges
255  * to destroy.
256  *
257  * Destroys a list of ranges returned from parse_cell_range_list.  Note:
258  * the element type here is GnmValue, not GnmRange.
259  **/
260 void
range_list_destroy(GSList * ranges)261 range_list_destroy (GSList *ranges)
262 {
263 	g_slist_free_full (ranges, (GDestroyNotify)value_release);
264 }
265 
266 
267 /**
268  * range_as_string:
269  * @r: A #GnmRange
270  *
271  * Returns: (transfer none): a string representation of @src
272  **/
273 char const *
range_as_string(GnmRange const * r)274 range_as_string (GnmRange const *r)
275 {
276 	static char buffer[(6 + 4 * sizeof (long)) * 2 + 1];
277 
278 	g_return_val_if_fail (r != NULL, "");
279 
280 	sprintf (buffer, "%s%s",
281 		 col_name (r->start.col), row_name (r->start.row));
282 
283 	if (r->start.col != r->end.col || r->start.row != r->end.row)
284 		sprintf (buffer + strlen(buffer), ":%s%s",
285 			 col_name (r->end.col), row_name (r->end.row));
286 
287 	return buffer;
288 }
289 
290 void
range_dump(GnmRange const * src,char const * suffix)291 range_dump (GnmRange const *src, char const *suffix)
292 {
293 	/*
294 	 * keep these as 2 print statements, because
295 	 * col_name and row_name use a static buffer
296 	 */
297 	g_printerr ("%s%s",
298 		col_name (src->start.col),
299 		row_name (src->start.row));
300 
301 	if (src->start.col != src->end.col ||
302 	    src->start.row != src->end.row)
303 		g_printerr (":%s%s",
304 			col_name (src->end.col),
305 			row_name (src->end.row));
306 	g_printerr ("%s", suffix);
307 }
308 
309 #ifdef RANGE_DEBUG
310 static void
ranges_dump(GList * l,char const * txt)311 ranges_dump (GList *l, char const *txt)
312 {
313 	g_printerr ("%s: ", txt);
314 	for (; l; l = l->next) {
315 		range_dump (l->data, "");
316 		if (l->next)
317 			g_printerr (", ");
318 	}
319 	g_printerr ("\n");
320 }
321 #endif
322 
323 /**
324  * range_contained:
325  * @a:
326  * @b:
327  *
328  * Is @a totally contained by @b
329  *
330  * Return value:
331  **/
332 gboolean
range_contained(GnmRange const * a,GnmRange const * b)333 range_contained (GnmRange const *a, GnmRange const *b)
334 {
335 	if (a->start.row < b->start.row)
336 		return FALSE;
337 
338 	if (a->end.row > b->end.row)
339 		return FALSE;
340 
341 	if (a->start.col < b->start.col)
342 		return FALSE;
343 
344 	if (a->end.col > b->end.col)
345 		return FALSE;
346 
347 	return TRUE;
348 }
349 
350 /**
351  * range_split_ranges:
352  * @hard: The region that is split against
353  * @soft: The region that is split
354  *
355  * Splits soft into several chunks, and returns the still
356  * overlapping remainder of soft as the first list item
357  * (the central region in the pathological case).
358  *
359  * Returns: (element-type GnmRange) (transfer full): A list of fragments.
360  **/
361 GSList *
range_split_ranges(GnmRange const * hard,GnmRange const * soft)362 range_split_ranges (GnmRange const *hard, GnmRange const *soft)
363 {
364 	/*
365 	 * There are lots of cases so think carefully.
366 	 *
367 	 * Original Methodology ( approximately )
368 	 *	a) Get a vertex: is it contained ?
369 	 *	b) Yes: split so it isn't
370 	 *	c) Continue for all vertices.
371 	 *
372 	 * NB. We prefer to have long columns at the expense
373 	 *     of long rows.
374 	 */
375 	GSList *split  = NULL;
376 	GnmRange *middle, *sp;
377 	gboolean split_left  = FALSE;
378 	gboolean split_right = FALSE;
379 
380 	g_return_val_if_fail (range_overlap (hard, soft), NULL);
381 
382 	middle = g_new (GnmRange, 1);
383 	*middle = *soft;
384 
385 	/* Split off left entirely */
386 	if (hard->start.col > soft->start.col) {
387 		sp = g_new (GnmRange, 1);
388 		sp->start.col = soft->start.col;
389 		sp->start.row = soft->start.row;
390 		sp->end.col   = hard->start.col - 1;
391 		sp->end.row   = soft->end.row;
392 		split = g_slist_prepend (split, sp);
393 
394 		middle->start.col = hard->start.col;
395 		split_left = TRUE;
396 	} /* else shared edge */
397 
398 	/* Split off right entirely */
399 	if (hard->end.col < soft->end.col) {
400 		sp = g_new (GnmRange, 1);
401 		sp->start.col = hard->end.col + 1;
402 		sp->start.row = soft->start.row;
403 		sp->end.col   = soft->end.col;
404 		sp->end.row   = soft->end.row;
405 		split = g_slist_prepend (split, sp);
406 
407 		middle->end.col = hard->end.col;
408 		split_right = TRUE;
409 	}  /* else shared edge */
410 
411 	/* Top */
412 	if (split_left && split_right) {
413 		if (hard->start.row > soft->start.row) {
414 			/* The top middle bit */
415 			sp = g_new (GnmRange, 1);
416 			sp->start.col = hard->start.col;
417 			sp->start.row = soft->start.row;
418 			sp->end.col   = hard->end.col;
419 			sp->end.row   = hard->start.row - 1;
420 			split = g_slist_prepend (split, sp);
421 
422 			middle->start.row = hard->start.row;
423 		} /* else shared edge */
424 	} else if (split_left) {
425 		if (hard->start.row > soft->start.row) {
426 			/* The top middle + right bits */
427 			sp = g_new (GnmRange, 1);
428 			sp->start.col = hard->start.col;
429 			sp->start.row = soft->start.row;
430 			sp->end.col   = soft->end.col;
431 			sp->end.row   = hard->start.row - 1;
432 			split = g_slist_prepend (split, sp);
433 
434 			middle->start.row = hard->start.row;
435 		} /* else shared edge */
436 	} else if (split_right) {
437 		if (hard->start.row > soft->start.row) {
438 			/* The top middle + left bits */
439 			sp = g_new (GnmRange, 1);
440 			sp->start.col = soft->start.col;
441 			sp->start.row = soft->start.row;
442 			sp->end.col   = hard->end.col;
443 			sp->end.row   = hard->start.row - 1;
444 			split = g_slist_prepend (split, sp);
445 
446 			middle->start.row = hard->start.row;
447 		} /* else shared edge */
448 	} else {
449 		if (hard->start.row > soft->start.row) {
450 			/* Hack off the top bit */
451 			sp = g_new (GnmRange, 1);
452 			sp->start.col = soft->start.col;
453 			sp->start.row = soft->start.row;
454 			sp->end.col   = soft->end.col;
455 			sp->end.row   = hard->start.row - 1;
456 			split = g_slist_prepend (split, sp);
457 
458 			middle->start.row = hard->start.row;
459 		} /* else shared edge */
460 	}
461 
462 	/* Bottom */
463 	if (split_left && split_right) {
464 		if (hard->end.row < soft->end.row) {
465 			/* The bottom middle bit */
466 			sp = g_new (GnmRange, 1);
467 			sp->start.col = hard->start.col;
468 			sp->start.row = hard->end.row + 1;
469 			sp->end.col   = hard->end.col;
470 			sp->end.row   = soft->end.row;
471 			split = g_slist_prepend (split, sp);
472 
473 			middle->end.row = hard->end.row;
474 		} /* else shared edge */
475 	} else if (split_left) {
476 		if (hard->end.row < soft->end.row) {
477 			/* The bottom middle + right bits */
478 			sp = g_new (GnmRange, 1);
479 			sp->start.col = hard->start.col;
480 			sp->start.row = hard->end.row + 1;
481 			sp->end.col   = soft->end.col;
482 			sp->end.row   = soft->end.row;
483 			split = g_slist_prepend (split, sp);
484 
485 			middle->end.row = hard->end.row;
486 		} /* else shared edge */
487 	} else if (split_right) {
488 		if (hard->end.row < soft->end.row) {
489 			/* The bottom middle + left bits */
490 			sp = g_new (GnmRange, 1);
491 			sp->start.col = soft->start.col;
492 			sp->start.row = hard->end.row + 1;
493 			sp->end.col   = hard->end.col;
494 			sp->end.row   = soft->end.row;
495 			split = g_slist_prepend (split, sp);
496 
497 			middle->end.row = hard->end.row;
498 		} /* else shared edge */
499 	} else {
500 		if (hard->end.row < soft->end.row) {
501 			/* Hack off the bottom bit */
502 			sp = g_new (GnmRange, 1);
503 			sp->start.col = soft->start.col;
504 			sp->start.row = hard->end.row + 1;
505 			sp->end.col   = soft->end.col;
506 			sp->end.row   = soft->end.row;
507 			split = g_slist_prepend (split, sp);
508 
509 			middle->end.row = hard->end.row;
510 		} /* else shared edge */
511 	}
512 
513 	return g_slist_prepend (split, middle);
514 }
515 
516 /**
517  * gnm_range_dup:
518  * @r: Source range to copy
519  *
520  * Copies the @r range.
521  *
522  * Returns: (transfer full): A copy of the GnmRange.
523  **/
524 GnmRange *
gnm_range_dup(GnmRange const * r)525 gnm_range_dup (GnmRange const *r)
526 {
527 	return g_memdup (r, sizeof (*r));
528 }
529 
530 /**
531  * range_fragment:
532  * @a: First #GnmRange
533  * @b: Second #GnmRange
534  *
535  * Fragments the ranges into totally non-overlapping regions,
536  *
537  * Returns: (element-type GnmRange) (transfer full): A list of fragmented
538  * ranges or at minimum simply @a and @b.
539  **/
540 GSList *
range_fragment(GnmRange const * a,GnmRange const * b)541 range_fragment (GnmRange const *a, GnmRange const *b)
542 {
543 	GSList *split, *ans = NULL;
544 
545 	split = range_split_ranges (a, b);
546 	ans   = g_slist_concat (ans, split);
547 
548 	split = range_split_ranges (b, a);
549 	if (split) {
550 		g_free (split->data);
551 		split = g_slist_remove (split, split->data);
552 	}
553 	ans = g_slist_concat (ans, split);
554 
555 	return ans;
556 }
557 
558 /**
559  * range_intersection:
560  * @r: intersection range
561  * @a: First #GnmRange
562  * @b: Second #GnmRange
563  *
564  * This computes the intersection of two ranges; on a Venn
565  * diagram this would be A (upside down U) B.
566  * If the ranges do not intersect, false is returned an the
567  * values of @r are unpredictable.
568  *
569  * NB. totally commutative
570  *
571  * Return value: %TRUE if the ranges intersect, %FALSE otherwise
572  **/
573 gboolean
range_intersection(GnmRange * r,GnmRange const * a,GnmRange const * b)574 range_intersection (GnmRange *r, GnmRange const *a, GnmRange const *b)
575 {
576 	if (!range_overlap (a, b)) {
577 		*r = *a; // Something
578 		return FALSE;
579 	}
580 
581 	r->start.col = MAX (a->start.col, b->start.col);
582 	r->start.row = MAX (a->start.row, b->start.row);
583 
584 	r->end.col = MIN (a->end.col, b->end.col);
585 	r->end.row = MIN (a->end.row, b->end.row);
586 
587 	return TRUE;
588 }
589 
590 /**
591  * range_normalize:
592  * @src: a range
593  *
594  * Ensures that start <= end for rows and cols.
595  **/
596 void
range_normalize(GnmRange * src)597 range_normalize (GnmRange *src)
598 {
599 	int tmp;
600 
601 	tmp = src->end.col;
602 	if (src->start.col > tmp) {
603 		src->end.col = src->start.col;
604 		src->start.col = tmp;
605 	}
606 	tmp = src->end.row;
607 	if (src->start.row > tmp) {
608 		src->end.row = src->start.row;
609 		src->start.row = tmp;
610 	}
611 }
612 
613 /**
614  * range_union:
615  * @a: range a
616  * @b: range b
617  *
618  * This computes the union; on a Venn
619  * diagram this would be A U B
620  * NB. totally commutative. Also, this may
621  * include cells not in either range since
622  * it must return a #GnmRange.
623  *
624  * Return value: the union
625  **/
626 GnmRange
range_union(GnmRange const * a,GnmRange const * b)627 range_union (GnmRange const *a, GnmRange const *b)
628 {
629 	GnmRange ans;
630 
631 	if (a->start.col < b->start.col)
632 		ans.start.col = a->start.col;
633 	else
634 		ans.start.col = b->start.col;
635 
636 	if (a->end.col > b->end.col)
637 		ans.end.col   = a->end.col;
638 	else
639 		ans.end.col   = b->end.col;
640 
641 	if (a->start.row < b->start.row)
642 		ans.start.row = a->start.row;
643 	else
644 		ans.start.row = b->start.row;
645 
646 	if (a->end.row > b->end.row)
647 		ans.end.row   = a->end.row;
648 	else
649 		ans.end.row   = b->end.row;
650 
651 	return ans;
652 }
653 
654 /**
655  * range_is_singleton:
656  * @r: the range.
657  *
658  * Returns: %TRUE if @r is a single-cell range.
659  */
660 gboolean
range_is_singleton(GnmRange const * r)661 range_is_singleton (GnmRange const *r)
662 {
663 	return r->start.col == r->end.col && r->start.row == r->end.row;
664 }
665 
666 /**
667  * range_is_full:
668  * @r: the range.
669  * @sheet: the sheet in which @r lives
670  * @horiz: %TRUE to check for a horizontal full ref (all _cols_); %FALSE
671  * to check for a vertical full ref (all _rows_).
672  *
673  * This determines whether @r completely spans a sheet in the dimension
674  * specified by @horiz.
675  *
676  * Returns: %TRUE if it is infinite, %FALSE otherwise.
677  **/
678 gboolean
range_is_full(GnmRange const * r,Sheet const * sheet,gboolean horiz)679 range_is_full (GnmRange const *r, Sheet const *sheet, gboolean horiz)
680 {
681 	if (horiz)
682 		return (r->start.col <= 0 &&
683 			r->end.col >= gnm_sheet_get_last_col (sheet));
684 	else
685 		return (r->start.row <= 0 &&
686 			r->end.row >= gnm_sheet_get_last_row (sheet));
687 }
688 
689 /**
690  * range_clip_to_finite:
691  * @range:
692  * @sheet: the sheet in which @range lives
693  *
694  * Clip the range to the area of the sheet with content.
695  * if the range reaches the edge
696  *
697  * The idea here is that users may select a whole column or row when they
698  * really are only concerned with the extent of the sheet.
699  * On the otehr hand, if users select any smaller region they probably
700  * intend to select just that.
701  *
702  * WARNING THIS IS EXPENSIVE!
703  **/
704 void
range_clip_to_finite(GnmRange * range,Sheet * sheet)705 range_clip_to_finite (GnmRange *range, Sheet *sheet)
706 {
707 	GnmRange extent;
708 
709 	/* FIXME : This seems expensive.  We should see if there is a faster
710 	 * way of doing this.  possibly using a flag for content changes, and
711 	 * using the current values as a cache
712 	 */
713 	extent = sheet_get_extent (sheet, FALSE, TRUE);
714 	if (range->end.col >= gnm_sheet_get_max_cols (sheet) - 1)
715 		range->end.col = extent.end.col;
716 	if (range->end.row >= gnm_sheet_get_max_rows (sheet) - 1)
717 		range->end.row = extent.end.row;
718 }
719 
720 /**
721  * range_width:
722  * @r: #GnmRange
723  *
724  * Returns: width of @r.
725  */
726 int
range_width(GnmRange const * r)727 range_width (GnmRange const *r)
728 {
729 	g_return_val_if_fail (r != NULL, 0);
730 	return ABS (r->end.col - r->start.col) + 1;
731 }
732 
733 /**
734  * range_height:
735  * @r: #GnmRange
736  *
737  * Returns: height of @r.
738  */
739 int
range_height(GnmRange const * r)740 range_height (GnmRange const *r)
741 {
742 	g_return_val_if_fail (r != NULL, 0);
743 	return ABS (r->end.row - r->start.row) + 1;
744 }
745 
746 /**
747  * range_translate:
748  * @range:
749  * @sheet: the sheet in which @range lives
750  * @col_offset:
751  * @row_offset:
752  *
753  * Translate the range and return %TRUE if it is invalidated.
754  *
755  * Return: %TRUE if the range is no longer valid.
756  **/
757 gboolean
range_translate(GnmRange * range,Sheet const * sheet,int col_offset,int row_offset)758 range_translate (GnmRange *range, Sheet const *sheet, int col_offset, int row_offset)
759 {
760 	/*
761 	 * FIXME: we should probably check for overflow without actually
762 	 * performing it.
763 	 */
764 	range->start.col += col_offset;
765 	range->end.col   += col_offset;
766 	range->start.row += row_offset;
767 	range->end.row   += row_offset;
768 
769 	/* check for completely out of bounds */
770 	if (range->start.col >= gnm_sheet_get_max_cols (sheet) || range->start.col < 0 ||
771 	    range->start.row >= gnm_sheet_get_max_rows (sheet) || range->start.row < 0 ||
772 	    range->end.col >= gnm_sheet_get_max_cols (sheet) || range->end.col < 0 ||
773 	    range->end.row >= gnm_sheet_get_max_rows (sheet) || range->end.row < 0)
774 		return TRUE;
775 
776 	return FALSE;
777 }
778 
779 /**
780  * range_ensure_sanity:
781  * @range: the range to check
782  * @sheet: the sheet in which @range lives
783  *
784  * Silently clip a range to ensure that it does not contain areas
785  * outside the valid bounds.  Does NOT fix inverted ranges.
786  **/
787 void
range_ensure_sanity(GnmRange * range,Sheet const * sheet)788 range_ensure_sanity (GnmRange *range, Sheet const *sheet)
789 {
790 	range->start.col = MAX (0, range->start.col);
791 	range->end.col = MIN (range->end.col, gnm_sheet_get_last_col (sheet));
792 
793 	range->start.row = MAX (0, range->start.row);
794 	range->end.row = MIN (range->end.row, gnm_sheet_get_last_row (sheet));
795 }
796 
797 /**
798  * range_is_sane:
799  * @range: the range to check
800  *
801  * Generate warnings if the range is out of bounds or inverted.
802  **/
803 gboolean
range_is_sane(GnmRange const * range)804 range_is_sane (GnmRange const *range)
805 {
806 	g_return_val_if_fail (range != NULL, FALSE);
807 	g_return_val_if_fail (range->start.col >= 0, FALSE);
808 	g_return_val_if_fail (range->end.col >= range->start.col, FALSE);
809 	g_return_val_if_fail (range->end.col <= G_MAXINT / 2, FALSE);
810 	g_return_val_if_fail (range->start.row >= 0, FALSE);
811 	g_return_val_if_fail (range->end.row >= range->start.row, FALSE);
812 	g_return_val_if_fail (range->end.row <= G_MAXINT / 2, FALSE);
813 
814 	return TRUE;
815 }
816 
817 /**
818  * range_transpose:
819  * @range: The range.
820  * @sheet: the sheet in which @range lives
821  * @origin: The box to transpose inside
822  *
823  * Effectively mirrors the ranges in 'boundary' around a
824  * leading diagonal projected from offset.
825  *
826  * Return value: whether we clipped the range.
827  **/
828 gboolean
range_transpose(GnmRange * range,Sheet const * sheet,GnmCellPos const * origin)829 range_transpose (GnmRange *range, Sheet const *sheet, GnmCellPos const *origin)
830 {
831 	gboolean clipped = FALSE;
832 	GnmRange src;
833 	int t;
834 	int last_col = gnm_sheet_get_last_col (sheet);
835 	int last_row = gnm_sheet_get_last_row (sheet);
836 
837 	g_return_val_if_fail (range != NULL, TRUE);
838 
839 	src = *range;
840 
841 	/* Start col */
842 	t = origin->col + (src.start.row - origin->row);
843 	if (t > last_col) {
844 		clipped = TRUE;
845 		range->start.col = last_col;
846 	} else if (t < 0) {
847 		clipped = TRUE;
848 		range->start.col = 0;
849 	}
850 		range->start.col = t;
851 
852 	/* Start row */
853 	t = origin->row + (src.start.col - origin->col);
854 	if (t > last_row) {
855 		clipped = TRUE;
856 		range->start.row = last_row;
857 	} else if (t < 0) {
858 		clipped = TRUE;
859 		range->start.row = 0;
860 	}
861 		range->start.row = t;
862 
863 
864 	/* End col */
865 	t = origin->col + (src.end.row - origin->row);
866 	if (t > last_col) {
867 		clipped = TRUE;
868 		range->end.col = last_col;
869 	} else if (t < 0) {
870 		clipped = TRUE;
871 		range->end.col = 0;
872 	}
873 		range->end.col = t;
874 
875 	/* End row */
876 	t = origin->row + (src.end.col - origin->col);
877 	if (t > last_row) {
878 		clipped = TRUE;
879 		range->end.row = last_row;
880 	} else if (t < 0) {
881 		clipped = TRUE;
882 		range->end.row = 0;
883 	}
884 		range->end.row = t;
885 
886 	g_assert (range_valid (range));
887 
888 	return clipped;
889 }
890 
891 gboolean
gnm_range_equal(const GnmRange * a,const GnmRange * b)892 gnm_range_equal (const GnmRange *a, const GnmRange *b)
893 {
894 	return range_equal (a, b);
895 }
896 
897 /**
898  * gnm_range_compare:
899  * @a: first range
900  * @b: second range
901  *
902  * Returns: a value that is negative if range @a comes before range @b;
903  * zero if the two ranges are equal; positive if range @a comes after
904  * range @b.  The order imposed is lexicographical by starting row,
905  * then column, then ending row, then column.  In other words, the order
906  * is A1, B1, A2, B2.
907  */
908 int
gnm_range_compare(GnmRange const * a,GnmRange const * b)909 gnm_range_compare (GnmRange const *a, GnmRange const *b)
910 {
911 	int i = 0;
912 	if (!i) i = a->start.row - b->start.row;
913 	if (!i) i = a->start.col - b->start.col;
914 	if (!i) i = a->end.row - b->end.row;
915 	if (!i) i = a->end.col - b->end.col;
916 	return i;
917 }
918 
919 // Alternative with order A1, A2, B1, B2
920 static int
gnm_range_compare_alt(GnmRange const * a,GnmRange const * b)921 gnm_range_compare_alt (GnmRange const *a, GnmRange const *b)
922 {
923 	int i = 0;
924 	if (!i) i = a->start.col - b->start.col;
925 	if (!i) i = a->start.row - b->start.row;
926 	if (!i) i = a->end.col - b->end.col;
927 	if (!i) i = a->end.row - b->end.row;
928 	return i;
929 }
930 
931 
932 GnmSheetRange *
gnm_sheet_range_new(Sheet * sheet,GnmRange const * r)933 gnm_sheet_range_new (Sheet *sheet, GnmRange const *r)
934 {
935 	GnmSheetRange *gr;
936 
937 	g_return_val_if_fail (IS_SHEET (sheet), NULL);
938 	g_return_val_if_fail (r != NULL, NULL);
939 
940 	gr = g_new0 (GnmSheetRange, 1);
941 	gr->sheet = sheet;
942 	gr->range = *r;
943 
944 	return gr;
945 }
946 
947 GnmSheetRange *
gnm_sheet_range_dup(GnmSheetRange const * sr)948 gnm_sheet_range_dup (GnmSheetRange const *sr)
949 {
950 	g_return_val_if_fail (sr != NULL, NULL);
951 	return gnm_sheet_range_new (sr->sheet, &sr->range);
952 }
953 
954 /**
955  * gnm_sheet_range_from_value:
956  * @r: #GnmSheetRange to change
957  * @v: #GnmValue containing a cell range.
958  *
959  * Convert @v into a GnmSheetRange.
960  *
961  * Returns: %TRUE
962  **/
963 gboolean
gnm_sheet_range_from_value(GnmSheetRange * r,GnmValue const * v)964 gnm_sheet_range_from_value (GnmSheetRange *r, GnmValue const *v)
965 {
966 	g_return_val_if_fail (VALUE_IS_CELLRANGE (v), FALSE);
967 
968 	r->sheet = v->v_range.cell.a.sheet;
969 	range_init_value (&r->range, v);
970 
971 	return TRUE;
972 }
973 
974 void
gnm_sheet_range_free(GnmSheetRange * gr)975 gnm_sheet_range_free (GnmSheetRange *gr)
976 {
977 	g_free (gr);
978 }
979 
980 GType
gnm_sheet_range_get_type(void)981 gnm_sheet_range_get_type (void)
982 {
983 	static GType t = 0;
984 
985 	if (t == 0)
986 		t = g_boxed_type_register_static ("GnmSheetRange",
987 			 (GBoxedCopyFunc)gnm_sheet_range_dup,
988 			 (GBoxedFreeFunc)gnm_sheet_range_free);
989 	return t;
990 }
991 
992 gboolean
gnm_sheet_range_equal(const GnmSheetRange * a,const GnmSheetRange * b)993 gnm_sheet_range_equal (const GnmSheetRange *a,
994 		       const GnmSheetRange *b)
995 {
996 	return a->sheet == b->sheet && range_equal (&a->range, &b->range);
997 }
998 
999 gboolean
gnm_sheet_range_overlap(GnmSheetRange const * a,GnmSheetRange const * b)1000 gnm_sheet_range_overlap (GnmSheetRange const *a, GnmSheetRange const *b)
1001 {
1002 	g_return_val_if_fail (a != NULL, FALSE);
1003 	g_return_val_if_fail (b != NULL, FALSE);
1004 
1005 	if (a->sheet == b->sheet && range_overlap (&a->range, &b->range))
1006 		return TRUE;
1007 
1008 	return FALSE;
1009 }
1010 
1011 char *
global_range_name(Sheet const * sheet,GnmRange const * r)1012 global_range_name (Sheet const *sheet, GnmRange const *r)
1013 {
1014 	char const * the_range_name = range_as_string (r);
1015 
1016 	if (sheet == NULL)
1017 		return g_strdup (the_range_name);
1018 
1019 	return g_strdup_printf ("%s!%s", sheet->name_quoted, the_range_name);
1020 }
1021 
1022 /**
1023  * undo_cell_pos_name:
1024  * @sheet:
1025  * @pos:
1026  *
1027  * Returns the range name depending on the preference setting.
1028  **/
1029 char *
undo_cell_pos_name(Sheet const * sheet,GnmCellPos const * pos)1030 undo_cell_pos_name (Sheet const *sheet, GnmCellPos const *pos)
1031 {
1032 	GnmRange r;
1033 	r.end = r.start = *pos;
1034 	return undo_range_name (sheet, &r);
1035 }
1036 
1037 /**
1038  * undo_range_name:
1039  * @sheet:
1040  * @r:
1041  *
1042  * Returns the range name depending on the preference setting.
1043  **/
1044 char *
undo_range_name(Sheet const * sheet,GnmRange const * r)1045 undo_range_name (Sheet const *sheet, GnmRange const *r)
1046 {
1047 	char const *the_range_name = range_as_string (r);
1048 
1049 	if (sheet != NULL && gnm_conf_get_undo_show_sheet_name ()) {
1050 		GString *str  = g_string_new (NULL);
1051 		gboolean truncated = FALSE;
1052 
1053 		g_string_printf (str, "%s!%s", sheet->name_quoted, the_range_name);
1054 		gnm_cmd_trunc_descriptor (str, &truncated);
1055 
1056 		if (!truncated)
1057 			return g_string_free (str, FALSE);
1058 
1059 		g_string_printf (str, UNICODE_ELLIPSIS "!%s", the_range_name);
1060 		gnm_cmd_trunc_descriptor (str, &truncated);
1061 
1062 		if (!truncated)
1063 			return g_string_free (str, FALSE);
1064 		g_string_free (str, TRUE);
1065 	}
1066 
1067 	return g_string_free
1068 		(gnm_cmd_trunc_descriptor (g_string_new (the_range_name), NULL), FALSE);
1069 }
1070 
1071 
1072 /*
1073  * Create range list name, but don't exceed max_width.
1074  * Returns: %TRUE iff the name is complete.
1075  */
1076 static gboolean
range_list_name_try(GString * names,char const * sheet,GSList const * ranges)1077 range_list_name_try (GString *names, char const *sheet, GSList const *ranges)
1078 {
1079 	GSList const *l;
1080 	char const *n = range_as_string (ranges->data);
1081 	gboolean truncated;
1082 
1083 	if (sheet == NULL)
1084 		g_string_assign (names, n);
1085 	else
1086 		g_string_printf (names, "%s!%s", sheet, n);
1087 
1088 	gnm_cmd_trunc_descriptor (names, &truncated);
1089 
1090 	if (truncated)
1091 		return FALSE;
1092 
1093 	for (l = ranges->next; l != NULL; l = l->next) {
1094 		n = range_as_string (l->data);
1095 
1096 		if (sheet == NULL)
1097 			g_string_append_printf (names, ", %s", n);
1098 		else
1099 			g_string_append_printf (names, ", %s!%s",
1100 						sheet, n);
1101 
1102 		gnm_cmd_trunc_descriptor (names, &truncated);
1103 
1104 		if (truncated)
1105 			return FALSE;
1106 	}
1107 
1108 	/* Have we reached the end? */
1109 	return TRUE;
1110 }
1111 
1112 
1113 /**
1114  * undo_range_list_name:
1115  * @sheet:
1116  * @ranges: (element-type GnmRange): list of ranges
1117  *
1118  * Returns: the range list name depending on the preference setting.
1119  * (The result will be something like: "A1:C3, D4:E5"). The string will be
1120  * truncated to max_descriptor_width.
1121  **/
1122 char *
undo_range_list_name(Sheet const * sheet,GSList const * ranges)1123 undo_range_list_name (Sheet const *sheet, GSList const *ranges)
1124 {
1125 	GString *names_with_sheet = NULL, *names_with_ellipsis, *names;
1126 
1127 	g_return_val_if_fail (ranges != NULL, NULL);
1128 
1129 	/* With the sheet name. */
1130 	if (sheet != NULL && gnm_conf_get_undo_show_sheet_name ()) {
1131 		names_with_sheet = g_string_new (NULL);
1132 		if (range_list_name_try (names_with_sheet, sheet->name_quoted, ranges)) {
1133 			/* We have reached the end, return the data from names. */
1134 			return g_string_free (names_with_sheet, FALSE);
1135 		}
1136 		names_with_ellipsis = g_string_new (NULL);
1137 		if (range_list_name_try (names_with_ellipsis, UNICODE_ELLIPSIS, ranges)) {
1138 			/* We have reached the end, return the data from names. */
1139 			g_string_free (names_with_sheet, TRUE);
1140 			return g_string_free (names_with_ellipsis, FALSE);
1141 		}
1142 		g_string_free (names_with_ellipsis, TRUE);
1143 	}
1144 
1145 	/* Without the sheet name. */
1146 	names = g_string_new (NULL);
1147 	if (range_list_name_try (names, NULL, ranges)) {
1148 		/* We have reached the end, return the data from names. */
1149 		if (names_with_sheet != NULL)
1150 			g_string_free (names_with_sheet, TRUE);
1151 		return g_string_free (names, FALSE);
1152 	}
1153 
1154 	/* We have to use a truncated version. */
1155 	if (names_with_sheet != NULL) {
1156 		g_string_free (names, TRUE);
1157 		return g_string_free (names_with_sheet, FALSE);
1158 	}
1159 	return g_string_free (names, FALSE);
1160 }
1161 
1162 /**
1163  * global_range_list_parse:
1164  * @sheet: Sheet where the range specification is relatively parsed to
1165  * @str: a range or list of ranges to parse (ex: "A1", "A1:B1,C2,Sheet2!D2:D4")
1166  *
1167  * Parses a list of ranges, relative to the @sheet and returns a list with the
1168  * results.
1169  *
1170  * Returns: (element-type GnmValue) (transfer full): a #GSList of #GnmValue.
1171  **/
1172 GSList *
global_range_list_parse(Sheet * sheet,char const * str)1173 global_range_list_parse (Sheet *sheet, char const *str)
1174 {
1175 	GnmParsePos  pp;
1176 	GnmExprTop const *texpr;
1177 	GSList   *ranges = NULL;
1178 	GnmValue	 *v;
1179 
1180 	g_return_val_if_fail (IS_SHEET (sheet), NULL);
1181 	g_return_val_if_fail (str != NULL, NULL);
1182 
1183 	texpr = gnm_expr_parse_str (str,
1184 		 parse_pos_init_sheet (&pp, sheet),
1185 		 GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES |
1186 		 GNM_EXPR_PARSE_PERMIT_MULTIPLE_EXPRESSIONS |
1187 		 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS,
1188 		 NULL, NULL);
1189 
1190 	if (texpr != NULL)  {
1191 		if (GNM_EXPR_GET_OPER (texpr->expr) == GNM_EXPR_OP_SET) {
1192 			GnmExpr const *expr = texpr->expr;
1193 			int i;
1194 			for (i = 0; i < expr->set.argc; i++) {
1195 				v = gnm_expr_get_range (expr->set.argv[i]);
1196 				if (v == NULL) {
1197 					range_list_destroy (ranges);
1198 					ranges = NULL;
1199 					break;
1200 				} else
1201 					ranges = g_slist_prepend (ranges, v);
1202 			}
1203 		} else {
1204 			v = gnm_expr_top_get_range (texpr);
1205 			if (v != NULL)
1206 				ranges = g_slist_prepend (ranges, v);
1207 		}
1208 		gnm_expr_top_unref (texpr);
1209 	}
1210 
1211 	return g_slist_reverse (ranges);
1212 }
1213 
1214 /**
1215  * global_range_list_foreach:
1216  * @gr_list: (element-type GnmRange): list of ranges.
1217  * @ep:
1218  * @flags:
1219  * @handler: (scope call):
1220  * @closure: user data.
1221  *
1222  * Returns: (transfer none):
1223  **/
1224 GnmValue *
global_range_list_foreach(GSList * gr_list,GnmEvalPos const * ep,CellIterFlags flags,CellIterFunc handler,gpointer closure)1225 global_range_list_foreach (GSList *gr_list, GnmEvalPos const *ep,
1226 			   CellIterFlags flags,
1227 			   CellIterFunc  handler,
1228 			   gpointer	 closure)
1229 {
1230 	GnmValue *v;
1231 	for (; gr_list != NULL; gr_list = gr_list->next) {
1232 		v = workbook_foreach_cell_in_range (ep, gr_list->data,
1233 			flags, handler, closure);
1234 		if (v != NULL)
1235 			return v;
1236 	}
1237 
1238 	return NULL;
1239 }
1240 
1241 
1242 /**
1243  * global_range_contained:
1244  * @sheet: The calling context #Sheet for references without sheet.
1245  * @a: A #GnmValue representing a range
1246  * @b: A #GnmValue representing a range
1247  *
1248  * Returns: %TRUE if @a is contained in @b.  We do not handle 3d ranges
1249  **/
1250 gboolean
global_range_contained(Sheet const * sheet,GnmValue const * a,GnmValue const * b)1251 global_range_contained (Sheet const *sheet, GnmValue const *a, GnmValue const *b)
1252 {
1253 	Sheet const *target;
1254 
1255 	g_return_val_if_fail (a != NULL, FALSE);
1256 	g_return_val_if_fail (b != NULL, FALSE);
1257 
1258 	if (!VALUE_IS_CELLRANGE (a) || !VALUE_IS_CELLRANGE (b))
1259 		return FALSE;
1260 
1261 	target = eval_sheet (a->v_range.cell.a.sheet, sheet);
1262 	if (target != eval_sheet (a->v_range.cell.b.sheet, sheet))
1263 		return FALSE;
1264 
1265 	if (target != eval_sheet (b->v_range.cell.a.sheet, sheet) ||
1266 	    target != eval_sheet (b->v_range.cell.b.sheet, sheet))
1267 		return FALSE;
1268 
1269 	if (a->v_range.cell.a.row < b->v_range.cell.a.row)
1270 		return FALSE;
1271 
1272 	if (a->v_range.cell.b.row > b->v_range.cell.b.row)
1273 		return FALSE;
1274 
1275 	if (a->v_range.cell.a.col < b->v_range.cell.a.col)
1276 		return FALSE;
1277 
1278 	if (a->v_range.cell.b.col > b->v_range.cell.b.col)
1279 		return FALSE;
1280 
1281 	return TRUE;
1282 }
1283 
1284 GType
gnm_range_get_type(void)1285 gnm_range_get_type (void)
1286 {
1287 	static GType t = 0;
1288 
1289 	if (t == 0)
1290 		t = g_boxed_type_register_static ("GnmRange",
1291 			 (GBoxedCopyFunc)gnm_range_dup,
1292 			 (GBoxedFreeFunc)g_free);
1293 	return t;
1294 }
1295 
1296 static gboolean
merge_ranges(GnmRange * a,GnmRange const * b)1297 merge_ranges (GnmRange *a, GnmRange const *b)
1298 {
1299 	if (a->start.row == b->start.row &&
1300 	    a->end.row == b->end.row &&
1301 	    a->end.col + 1 >= b->start.col) {
1302 		// "a" is just left of "b", possibly with overlap
1303 		a->end.col = MAX (a->end.col, b->end.col);
1304 		return TRUE;
1305 	}
1306 
1307 	if (a->start.col == b->start.col &&
1308 	    a->end.col == b->end.col &&
1309 	    a->end.row + 1 >= b->start.row) {
1310 		// "a" is just on top of "b", possibly with overlap
1311 		a->end.row = MAX (a->end.row, b->end.row);
1312 		return TRUE;
1313 	}
1314 
1315 	if (range_contained (b, a)) {
1316 		// "b" is inside "a"
1317 		return TRUE;
1318 	}
1319 
1320 	// Punt.
1321 	return FALSE;
1322 }
1323 
1324 static gboolean
try_merge_pair(GArray * arr,unsigned ui1,unsigned ui2)1325 try_merge_pair (GArray *arr, unsigned ui1, unsigned ui2)
1326 {
1327 	GnmRange *ra = &g_array_index (arr, GnmRange, ui1);
1328 	GnmRange *rb = &g_array_index (arr, GnmRange, ui2);
1329 
1330 	if (merge_ranges (ra, rb)) {
1331 		g_array_remove_index (arr, ui2);
1332 		return TRUE;
1333 	} else
1334 		return FALSE;
1335 }
1336 
1337 /**
1338  * gnm_range_simplify:
1339  * @arr: (element-type GnmRange) (inout): array of ranges to simplify
1340  *
1341  * Simplifies the array of ranges by merging small ranges into larger ones.
1342  */
1343 void
gnm_range_simplify(GArray * arr)1344 gnm_range_simplify (GArray *arr)
1345 {
1346 	unsigned ui;
1347 
1348 	if (arr->len < 2)
1349 		return;
1350 
1351 	g_array_sort (arr, (GCompareFunc) gnm_range_compare);
1352 	// Two cheap passes through the ranges.
1353 	for (ui = arr->len - 1; ui > 0; ui--)
1354 		try_merge_pair (arr, ui - 1, ui);
1355 	for (ui = arr->len - 1; ui > 0; ui--)
1356 		try_merge_pair (arr, ui - 1, ui);
1357 
1358 	g_array_sort (arr, (GCompareFunc) gnm_range_compare_alt);
1359 	for (ui = arr->len - 1; ui > 0; ui--)
1360 		try_merge_pair (arr, ui - 1, ui);
1361 }
1362