1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * ranges.c: various functions for common operations on cell ranges.
6 * Jody Goldberg (jody@gnome.org)
7 * Miguel de Icaza (miguel@gnu.org).
8 * Michael Meeks (mmeeks@gnu.org).
9 * Copyright (C) 2007-2009 Morten Welinder (terra@gnome.org)
11 #include <gnumeric-config.h>
18 #include "expr-impl.h"
19 #include "expr-name.h"
21 #include "sheet-style.h"
22 #include "parse-util.h"
27 #include "gnumeric-conf.h"
31 #include <glib/gi18n-lib.h>
35 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
38 * range_init_full_sheet:
42 * Updates @r to fill @sheet in its entirety.
44 * Returns: (transfer none): @r
47 range_init_full_sheet (GnmRange
*r
, Sheet
const *sheet
)
51 r
->end
.col
= gnm_sheet_get_last_col (sheet
);
52 r
->end
.row
= gnm_sheet_get_last_row (sheet
);
60 * @start_col: Starting column
61 * @end_col: Ending column
63 * Updates @r to span full columns @start_col through @end_col completely.
65 * Returns: (transfer none): @r
68 range_init_cols (GnmRange
*r
, Sheet
const *sheet
, int start_col
, int end_col
)
70 r
->start
.col
= start_col
;
73 r
->end
.row
= gnm_sheet_get_last_row (sheet
);
81 * @start_row: Starting row
82 * @end_row: Ending row
84 * Updates @r to span full rows @start_row through @end_row completely.
86 * Returns: (transfer none): @r
89 range_init_rows (GnmRange
*r
, Sheet
const *sheet
, int start_row
, int end_row
)
92 r
->start
.row
= start_row
;
93 r
->end
.col
= gnm_sheet_get_last_col (sheet
);
99 * range_init_invalid: (skip)
102 * Updates @r to a meaningless range
104 * Returns: (transfer none): @r
107 range_init_invalid (GnmRange
*r
)
117 * range_init_rangeref:
121 * Updates @r to be the same as the range part of @rr.
123 * Returns: (transfer none): @r
126 range_init_rangeref (GnmRange
*range
, GnmRangeRef
const *rr
)
128 g_return_val_if_fail (range
!= NULL
&& rr
!= NULL
, NULL
);
130 range
->start
.col
= rr
->a
.col
;
131 range
->start
.row
= rr
->a
.row
;
132 range
->end
.col
= rr
->b
.col
;
133 range
->end
.row
= rr
->b
.row
;
140 * @r: A #GnmRange to change
141 * @v: A #GnmValue containing a cell range
143 * Updates @r to be the same as the cell range of @v.
145 * Returns: (transfer none) (nullable): @r
148 range_init_value (GnmRange
*range
, GnmValue
const *v
)
150 g_return_val_if_fail (range
!= NULL
, NULL
);
151 g_return_val_if_fail (v
!= NULL
&& VALUE_IS_CELLRANGE (v
), NULL
);
153 return range_init_rangeref (range
, &v
->v_range
.cell
);
157 * range_init_cellpos:
158 * @r: A #GnmRange to change
159 * @pos: A #GnmCellPos
161 * Updates @r to be the singleton range of @pos
163 * Returns: (transfer none): @r
166 range_init_cellpos (GnmRange
*r
, GnmCellPos
const *pos
)
175 * range_init_cellpos_size:
176 * @r: A #GnmRange to change
177 * @start: A #GnmCellPos for the upper left corner of the desired range
178 * @cols: number of columns
179 * @rows: number of rows
181 * Updates @r to start at @start and spanning @cols columns and @rows rows.
183 * Returns: (transfer none): @r
186 range_init_cellpos_size (GnmRange
*r
,
187 GnmCellPos
const *start
, int cols
, int rows
)
190 r
->end
.col
= start
->col
+ cols
- 1;
191 r
->end
.row
= start
->row
+ rows
- 1;
198 * @r: A #GnmRange to change
204 * Updates @r to start at (@start_col,@start_row) and end
205 * at (@end_col,@end_row).
207 * Returns: (transfer none): @r
210 range_init (GnmRange
*r
, int start_col
, int start_row
,
211 int end_col
, int end_row
)
213 g_return_val_if_fail (r
!= NULL
, r
);
215 r
->start
.col
= start_col
;
216 r
->start
.row
= start_row
;
217 r
->end
.col
= end_col
;
218 r
->end
.row
= end_row
;
226 * @text: text to parse
227 * @ss: #GnmSheetSize describing the size of the sheet in which @r lives.
229 * Parse a simple range (no abs/rel refs, no sheet refs)
230 * Store the result in @r.
232 * Returns: %TRUE on success.
235 range_parse (GnmRange
*r
, char const *text
, GnmSheetSize
const *ss
)
237 text
= cellpos_parse (text
, ss
, &r
->start
, FALSE
);
249 text
= cellpos_parse (text
+ 1, ss
, &r
->end
, TRUE
);
254 * range_list_destroy:
255 * @ranges: (element-type GnmValue) (transfer full): a list of value ranges
258 * Destroys a list of ranges returned from parse_cell_range_list
261 range_list_destroy (GSList
*ranges
)
265 for (l
= ranges
; l
; l
= l
->next
){
266 GnmValue
*value
= l
->data
;
268 value_release (value
);
270 g_slist_free (ranges
);
278 * Returns: (transfer none): a string repesentation of @src
281 range_as_string (GnmRange
const *r
)
283 static char buffer
[(6 + 4 * sizeof (long)) * 2 + 1];
285 g_return_val_if_fail (r
!= NULL
, "");
287 sprintf (buffer
, "%s%s",
288 col_name (r
->start
.col
), row_name (r
->start
.row
));
290 if (r
->start
.col
!= r
->end
.col
|| r
->start
.row
!= r
->end
.row
)
291 sprintf (buffer
+ strlen(buffer
), ":%s%s",
292 col_name (r
->end
.col
), row_name (r
->end
.row
));
298 range_dump (GnmRange
const *src
, char const *suffix
)
301 * keep these as 2 print statements, because
302 * col_name and row_name use a static buffer
305 col_name (src
->start
.col
),
306 row_name (src
->start
.row
));
308 if (src
->start
.col
!= src
->end
.col
||
309 src
->start
.row
!= src
->end
.row
)
311 col_name (src
->end
.col
),
312 row_name (src
->end
.row
));
313 g_printerr ("%s", suffix
);
318 ranges_dump (GList
*l
, char const *txt
)
320 g_printerr ("%s: ", txt
);
321 for (; l
; l
= l
->next
) {
322 range_dump (l
->data
, "");
335 * Is @a totaly contained by @b
340 range_contained (GnmRange
const *a
, GnmRange
const *b
)
342 if (a
->start
.row
< b
->start
.row
)
345 if (a
->end
.row
> b
->end
.row
)
348 if (a
->start
.col
< b
->start
.col
)
351 if (a
->end
.col
> b
->end
.col
)
358 * range_split_ranges:
359 * @hard: The region that is split against
360 * @soft: The region that is split
362 * Splits soft into several chunks, and returns the still
363 * overlapping remainder of soft as the first list item
364 * ( the central region in the pathalogical case ).
366 * Returns: (element-type GnmRange) (transfer full): A list of fragments.
369 range_split_ranges (GnmRange
const *hard
, GnmRange
const *soft
)
372 * There are lots of cases so think carefully.
374 * Original Methodology ( approximately )
375 * a) Get a vertex: is it contained ?
376 * b) Yes: split so it isn't
377 * c) Continue for all verticees.
379 * NB. We prefer to have long columns at the expense
382 GSList
*split
= NULL
;
383 GnmRange
*middle
, *sp
;
384 gboolean split_left
= FALSE
;
385 gboolean split_right
= FALSE
;
387 g_return_val_if_fail (range_overlap (hard
, soft
), NULL
);
389 middle
= g_new (GnmRange
, 1);
392 /* Split off left entirely */
393 if (hard
->start
.col
> soft
->start
.col
) {
394 sp
= g_new (GnmRange
, 1);
395 sp
->start
.col
= soft
->start
.col
;
396 sp
->start
.row
= soft
->start
.row
;
397 sp
->end
.col
= hard
->start
.col
- 1;
398 sp
->end
.row
= soft
->end
.row
;
399 split
= g_slist_prepend (split
, sp
);
401 middle
->start
.col
= hard
->start
.col
;
403 } /* else shared edge */
405 /* Split off right entirely */
406 if (hard
->end
.col
< soft
->end
.col
) {
407 sp
= g_new (GnmRange
, 1);
408 sp
->start
.col
= hard
->end
.col
+ 1;
409 sp
->start
.row
= soft
->start
.row
;
410 sp
->end
.col
= soft
->end
.col
;
411 sp
->end
.row
= soft
->end
.row
;
412 split
= g_slist_prepend (split
, sp
);
414 middle
->end
.col
= hard
->end
.col
;
416 } /* else shared edge */
419 if (split_left
&& split_right
) {
420 if (hard
->start
.row
> soft
->start
.row
) {
421 /* The top middle bit */
422 sp
= g_new (GnmRange
, 1);
423 sp
->start
.col
= hard
->start
.col
;
424 sp
->start
.row
= soft
->start
.row
;
425 sp
->end
.col
= hard
->end
.col
;
426 sp
->end
.row
= hard
->start
.row
- 1;
427 split
= g_slist_prepend (split
, sp
);
429 middle
->start
.row
= hard
->start
.row
;
430 } /* else shared edge */
431 } else if (split_left
) {
432 if (hard
->start
.row
> soft
->start
.row
) {
433 /* The top middle + right bits */
434 sp
= g_new (GnmRange
, 1);
435 sp
->start
.col
= hard
->start
.col
;
436 sp
->start
.row
= soft
->start
.row
;
437 sp
->end
.col
= soft
->end
.col
;
438 sp
->end
.row
= hard
->start
.row
- 1;
439 split
= g_slist_prepend (split
, sp
);
441 middle
->start
.row
= hard
->start
.row
;
442 } /* else shared edge */
443 } else if (split_right
) {
444 if (hard
->start
.row
> soft
->start
.row
) {
445 /* The top middle + left bits */
446 sp
= g_new (GnmRange
, 1);
447 sp
->start
.col
= soft
->start
.col
;
448 sp
->start
.row
= soft
->start
.row
;
449 sp
->end
.col
= hard
->end
.col
;
450 sp
->end
.row
= hard
->start
.row
- 1;
451 split
= g_slist_prepend (split
, sp
);
453 middle
->start
.row
= hard
->start
.row
;
454 } /* else shared edge */
456 if (hard
->start
.row
> soft
->start
.row
) {
457 /* Hack off the top bit */
458 sp
= g_new (GnmRange
, 1);
459 sp
->start
.col
= soft
->start
.col
;
460 sp
->start
.row
= soft
->start
.row
;
461 sp
->end
.col
= soft
->end
.col
;
462 sp
->end
.row
= hard
->start
.row
- 1;
463 split
= g_slist_prepend (split
, sp
);
465 middle
->start
.row
= hard
->start
.row
;
466 } /* else shared edge */
470 if (split_left
&& split_right
) {
471 if (hard
->end
.row
< soft
->end
.row
) {
472 /* The bottom middle bit */
473 sp
= g_new (GnmRange
, 1);
474 sp
->start
.col
= hard
->start
.col
;
475 sp
->start
.row
= hard
->end
.row
+ 1;
476 sp
->end
.col
= hard
->end
.col
;
477 sp
->end
.row
= soft
->end
.row
;
478 split
= g_slist_prepend (split
, sp
);
480 middle
->end
.row
= hard
->end
.row
;
481 } /* else shared edge */
482 } else if (split_left
) {
483 if (hard
->end
.row
< soft
->end
.row
) {
484 /* The bottom middle + right bits */
485 sp
= g_new (GnmRange
, 1);
486 sp
->start
.col
= hard
->start
.col
;
487 sp
->start
.row
= hard
->end
.row
+ 1;
488 sp
->end
.col
= soft
->end
.col
;
489 sp
->end
.row
= soft
->end
.row
;
490 split
= g_slist_prepend (split
, sp
);
492 middle
->end
.row
= hard
->end
.row
;
493 } /* else shared edge */
494 } else if (split_right
) {
495 if (hard
->end
.row
< soft
->end
.row
) {
496 /* The bottom middle + left bits */
497 sp
= g_new (GnmRange
, 1);
498 sp
->start
.col
= soft
->start
.col
;
499 sp
->start
.row
= hard
->end
.row
+ 1;
500 sp
->end
.col
= hard
->end
.col
;
501 sp
->end
.row
= soft
->end
.row
;
502 split
= g_slist_prepend (split
, sp
);
504 middle
->end
.row
= hard
->end
.row
;
505 } /* else shared edge */
507 if (hard
->end
.row
< soft
->end
.row
) {
508 /* Hack off the bottom bit */
509 sp
= g_new (GnmRange
, 1);
510 sp
->start
.col
= soft
->start
.col
;
511 sp
->start
.row
= hard
->end
.row
+ 1;
512 sp
->end
.col
= soft
->end
.col
;
513 sp
->end
.row
= soft
->end
.row
;
514 split
= g_slist_prepend (split
, sp
);
516 middle
->end
.row
= hard
->end
.row
;
517 } /* else shared edge */
520 return g_slist_prepend (split
, middle
);
525 * @r: Source range to copy
527 * Copies the @r range.
529 * Returns: (transfer full): A copy of the GnmRange.
532 gnm_range_dup (GnmRange
const *a
)
534 GnmRange
*r
= g_new (GnmRange
, 1);
544 * Fragments the ranges into totaly non-overlapping regions,
546 * Returns: (element-type GnmRange) (transfer full): A list of fragmented
547 * ranges or at minimum simply a and b.
550 range_fragment (GnmRange
const *a
, GnmRange
const *b
)
552 GSList
*split
, *ans
= NULL
;
554 split
= range_split_ranges (a
, b
);
555 ans
= g_slist_concat (ans
, split
);
557 split
= range_split_ranges (b
, a
);
559 g_free (split
->data
);
560 split
= g_slist_remove (split
, split
->data
);
562 ans
= g_slist_concat (ans
, split
);
568 * range_intersection:
569 * @r: intersection range
573 * This computes the intersection of two ranges; on a Venn
574 * diagram this would be A (upside down U) B.
575 * If the ranges do not intersect, false is returned an the
576 * values of r are unpredictable.
578 * NB. totally commutative
580 * Return value: %TRUE if the ranges intersect, %FALSE otherwise
583 range_intersection (GnmRange
*r
, GnmRange
const *a
, GnmRange
const *b
)
585 if (!range_overlap (a
, b
)) {
586 *r
= *a
; // Something
590 r
->start
.col
= MAX (a
->start
.col
, b
->start
.col
);
591 r
->start
.row
= MAX (a
->start
.row
, b
->start
.row
);
593 r
->end
.col
= MIN (a
->end
.col
, b
->end
.col
);
594 r
->end
.row
= MIN (a
->end
.row
, b
->end
.row
);
603 * Ensures that start <= end for rows and cols.
606 range_normalize (GnmRange
*src
)
611 if (src
->start
.col
> tmp
) {
612 src
->end
.col
= src
->start
.col
;
613 src
->start
.col
= tmp
;
616 if (src
->start
.row
> tmp
) {
617 src
->end
.row
= src
->start
.row
;
618 src
->start
.row
= tmp
;
627 * This computes the union; on a Venn
628 * diagram this would be A U B
629 * NB. totally commutative. Also, this may
630 * include cells not in either range since
631 * it must return a GnmRange.
633 * Return value: the union
636 range_union (GnmRange
const *a
, GnmRange
const *b
)
640 if (a
->start
.col
< b
->start
.col
)
641 ans
.start
.col
= a
->start
.col
;
643 ans
.start
.col
= b
->start
.col
;
645 if (a
->end
.col
> b
->end
.col
)
646 ans
.end
.col
= a
->end
.col
;
648 ans
.end
.col
= b
->end
.col
;
650 if (a
->start
.row
< b
->start
.row
)
651 ans
.start
.row
= a
->start
.row
;
653 ans
.start
.row
= b
->start
.row
;
655 if (a
->end
.row
> b
->end
.row
)
656 ans
.end
.row
= a
->end
.row
;
658 ans
.end
.row
= b
->end
.row
;
664 * range_is_singleton:
667 * Returns: %TRUE if @r is a single-cell range.
670 range_is_singleton (GnmRange
const *r
)
672 return r
->start
.col
== r
->end
.col
&& r
->start
.row
== r
->end
.row
;
678 * @sheet: the sheet in which @r lives
679 * @horiz: %TRUE to check for a horizontal full ref (_cols_ [0..MAX))
681 * This determines whether @r completely spans a sheet
682 * in the dimension specified by @horiz.
684 * Return value: %TRUE if it is infinite else %FALSE.
687 range_is_full (GnmRange
const *r
, Sheet
const *sheet
, gboolean horiz
)
690 return (r
->start
.col
<= 0 &&
691 r
->end
.col
>= gnm_sheet_get_last_col (sheet
));
693 return (r
->start
.row
<= 0 &&
694 r
->end
.row
>= gnm_sheet_get_last_row (sheet
));
698 * range_clip_to_finite:
700 * @sheet: the sheet in which @range lives
702 * Clip the range to the area of the sheet with content.
703 * if the range reaches the edge
705 * The idea here is that users may select a whole column or row when they
706 * really are only concerned with the extent of the sheet.
707 * On the otehr hand, if users select any smaller region they probably
708 * intend to selec tjust that.
710 * WARNING THIS IS EXPENSIVE!
713 range_clip_to_finite (GnmRange
*range
, Sheet
*sheet
)
717 /* FIXME : This seems expensive. We should see if there is a faster
718 * way of doing this. possibly using a flag for content changes, and
719 * using the current values as a cache
721 extent
= sheet_get_extent (sheet
, FALSE
, TRUE
);
722 if (range
->end
.col
>= gnm_sheet_get_max_cols (sheet
) - 1)
723 range
->end
.col
= extent
.end
.col
;
724 if (range
->end
.row
>= gnm_sheet_get_max_rows (sheet
) - 1)
725 range
->end
.row
= extent
.end
.row
;
729 range_width (GnmRange
const *r
)
731 g_return_val_if_fail (r
!= NULL
, 0);
732 return ABS (r
->end
.col
- r
->start
.col
) + 1;
736 range_height (GnmRange
const *r
)
738 g_return_val_if_fail (r
!= NULL
, 0);
739 return ABS (r
->end
.row
- r
->start
.row
) + 1;
745 * @sheet: the sheet in which @range lives
749 * Translate the range and return TRUE if it is invalidated.
751 * return TRUE if the range is no longer valid.
754 range_translate (GnmRange
*range
, Sheet
const *sheet
, int col_offset
, int row_offset
)
757 * FIXME: we should probably check for overflow without actually
760 range
->start
.col
+= col_offset
;
761 range
->end
.col
+= col_offset
;
762 range
->start
.row
+= row_offset
;
763 range
->end
.row
+= row_offset
;
765 /* check for completely out of bounds */
766 if (range
->start
.col
>= gnm_sheet_get_max_cols (sheet
) || range
->start
.col
< 0 ||
767 range
->start
.row
>= gnm_sheet_get_max_rows (sheet
) || range
->start
.row
< 0 ||
768 range
->end
.col
>= gnm_sheet_get_max_cols (sheet
) || range
->end
.col
< 0 ||
769 range
->end
.row
>= gnm_sheet_get_max_rows (sheet
) || range
->end
.row
< 0)
776 * range_ensure_sanity:
777 * @range: the range to check
778 * @sheet: the sheet in which @range lives
780 * Silently clip a range to ensure that it does not contain areas
781 * outside the valid bounds. Does NOT fix inverted ranges.
784 range_ensure_sanity (GnmRange
*range
, Sheet
const *sheet
)
786 range
->start
.col
= MAX (0, range
->start
.col
);
787 range
->end
.col
= MIN (range
->end
.col
, gnm_sheet_get_last_col (sheet
));
789 range
->start
.row
= MAX (0, range
->start
.row
);
790 range
->end
.row
= MIN (range
->end
.row
, gnm_sheet_get_last_row (sheet
));
795 * @range: the range to check
797 * Generate warnings if the range is out of bounds or inverted.
800 range_is_sane (GnmRange
const *range
)
802 g_return_val_if_fail (range
!= NULL
, FALSE
);
803 g_return_val_if_fail (range
->start
.col
>= 0, FALSE
);
804 g_return_val_if_fail (range
->end
.col
>= range
->start
.col
, FALSE
);
805 g_return_val_if_fail (range
->end
.col
<= G_MAXINT
/ 2, FALSE
);
806 g_return_val_if_fail (range
->start
.row
>= 0, FALSE
);
807 g_return_val_if_fail (range
->end
.row
>= range
->start
.row
, FALSE
);
808 g_return_val_if_fail (range
->end
.row
<= G_MAXINT
/ 2, FALSE
);
816 * @sheet: the sheet in which @range lives
817 * @origin: The box to transpose inside
819 * Effectively mirrors the ranges in 'boundary' around a
820 * leading diagonal projected from offset.
822 * Return value: whether we clipped the range.
825 range_transpose (GnmRange
*range
, Sheet
const *sheet
, GnmCellPos
const *origin
)
827 gboolean clipped
= FALSE
;
830 int last_col
= gnm_sheet_get_last_col (sheet
);
831 int last_row
= gnm_sheet_get_last_row (sheet
);
833 g_return_val_if_fail (range
!= NULL
, TRUE
);
838 t
= origin
->col
+ (src
.start
.row
- origin
->row
);
841 range
->start
.col
= last_col
;
844 range
->start
.col
= 0;
846 range
->start
.col
= t
;
849 t
= origin
->row
+ (src
.start
.col
- origin
->col
);
852 range
->start
.row
= last_row
;
855 range
->start
.row
= 0;
857 range
->start
.row
= t
;
861 t
= origin
->col
+ (src
.end
.row
- origin
->row
);
864 range
->end
.col
= last_col
;
872 t
= origin
->row
+ (src
.end
.col
- origin
->col
);
875 range
->end
.row
= last_row
;
882 g_assert (range_valid (range
));
888 gnm_range_equal (const GnmRange
*a
, const GnmRange
*b
)
890 return range_equal (a
, b
);
898 * Returns: a value that is negative if range @a comes before range @b;
899 * zero if the two ranges are equal; positive if range @a comes after
900 * range @b. The order imposed is lexicographical by starting row,
901 * then column, then ending row, then column.
904 gnm_range_compare (GnmRange
const *a
, GnmRange
const *b
)
907 if (!i
) i
= a
->start
.row
- b
->start
.row
;
908 if (!i
) i
= a
->start
.col
- b
->start
.col
;
909 if (!i
) i
= a
->end
.row
- b
->end
.row
;
910 if (!i
) i
= a
->end
.col
- b
->end
.col
;
916 gnm_sheet_range_new (Sheet
*sheet
, GnmRange
const *r
)
920 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
921 g_return_val_if_fail (r
!= NULL
, NULL
);
923 gr
= g_new0 (GnmSheetRange
, 1);
931 gnm_sheet_range_dup (GnmSheetRange
const *sr
)
933 g_return_val_if_fail (sr
!= NULL
, NULL
);
934 return gnm_sheet_range_new (sr
->sheet
, &sr
->range
);
938 * gnm_sheet_range_from_value:
939 * @r: #GnmSheetRange to change
940 * @v: #GnmValue containing a cell range.
942 * Convert @v into a GnmSheetRange.
947 gnm_sheet_range_from_value (GnmSheetRange
*r
, GnmValue
const *v
)
949 g_return_val_if_fail (VALUE_IS_CELLRANGE (v
), FALSE
);
951 r
->sheet
= v
->v_range
.cell
.a
.sheet
;
952 range_init_value (&r
->range
, v
);
958 gnm_sheet_range_free (GnmSheetRange
*gr
)
964 gnm_sheet_range_get_type (void)
969 t
= g_boxed_type_register_static ("GnmSheetRange",
970 (GBoxedCopyFunc
)gnm_sheet_range_dup
,
971 (GBoxedFreeFunc
)gnm_sheet_range_free
);
976 gnm_sheet_range_equal (const GnmSheetRange
*a
,
977 const GnmSheetRange
*b
)
979 return a
->sheet
== b
->sheet
&& range_equal (&a
->range
, &b
->range
);
983 gnm_sheet_range_overlap (GnmSheetRange
const *a
, GnmSheetRange
const *b
)
985 g_return_val_if_fail (a
!= NULL
, FALSE
);
986 g_return_val_if_fail (b
!= NULL
, FALSE
);
988 if (a
->sheet
== b
->sheet
&& range_overlap (&a
->range
, &b
->range
))
995 global_range_name (Sheet
const *sheet
, GnmRange
const *r
)
997 char const * the_range_name
= range_as_string (r
);
1000 return g_strdup (the_range_name
);
1002 return g_strdup_printf ("%s!%s", sheet
->name_quoted
, the_range_name
);
1006 * undo_cell_pos_name:
1010 * Returns the range name depending on the preference setting.
1013 undo_cell_pos_name (Sheet
const *sheet
, GnmCellPos
const *pos
)
1016 r
.end
= r
.start
= *pos
;
1017 return undo_range_name (sheet
, &r
);
1025 * Returns the range name depending on the preference setting.
1028 undo_range_name (Sheet
const *sheet
, GnmRange
const *r
)
1030 char const *the_range_name
= range_as_string (r
);
1032 if (sheet
!= NULL
&& gnm_conf_get_undo_show_sheet_name ()) {
1033 GString
*str
= g_string_new (NULL
);
1034 gboolean truncated
= FALSE
;
1036 g_string_printf (str
, "%s!%s", sheet
->name_quoted
, the_range_name
);
1037 gnm_cmd_trunc_descriptor (str
, &truncated
);
1040 return g_string_free (str
, FALSE
);
1042 g_string_printf (str
, UNICODE_ELLIPSIS
"!%s", the_range_name
);
1043 gnm_cmd_trunc_descriptor (str
, &truncated
);
1046 return g_string_free (str
, FALSE
);
1047 g_string_free (str
, TRUE
);
1050 return g_string_free
1051 (gnm_cmd_trunc_descriptor (g_string_new (the_range_name
), NULL
), FALSE
);
1056 * Create range list name, but don't exceed max_width.
1057 * Return TRUE iff the name is complete.
1060 range_list_name_try (GString
*names
, char const *sheet
, GSList
const *ranges
)
1063 char const *n
= range_as_string (ranges
->data
);
1067 g_string_assign (names
, n
);
1069 g_string_printf (names
, "%s!%s", sheet
, n
);
1071 gnm_cmd_trunc_descriptor (names
, &truncated
);
1076 for (l
= ranges
->next
; l
!= NULL
; l
= l
->next
) {
1077 n
= range_as_string (l
->data
);
1080 g_string_append_printf (names
, ", %s", n
);
1082 g_string_append_printf (names
, ", %s!%s",
1085 gnm_cmd_trunc_descriptor (names
, &truncated
);
1091 /* Have we reached the end? */
1097 * undo_range_list_name:
1099 * @ranges: (element-type GnmRange): GSList containing GnmRange *'s
1101 * Returns the range list name depending on the preference setting.
1102 * (The result will be something like: "A1:C3, D4:E5"). The string will be
1103 * truncated to max_descriptor_width.
1106 undo_range_list_name (Sheet
const *sheet
, GSList
const *ranges
)
1108 GString
*names_with_sheet
= NULL
, *names_with_ellipsis
, *names
;
1110 g_return_val_if_fail (ranges
!= NULL
, NULL
);
1112 /* With the sheet name. */
1113 if (sheet
!= NULL
&& gnm_conf_get_undo_show_sheet_name ()) {
1114 names_with_sheet
= g_string_new (NULL
);
1115 if (range_list_name_try (names_with_sheet
, sheet
->name_quoted
, ranges
)) {
1116 /* We have reached the end, return the data from names. */
1117 return g_string_free (names_with_sheet
, FALSE
);
1119 names_with_ellipsis
= g_string_new (NULL
);
1120 if (range_list_name_try (names_with_ellipsis
, UNICODE_ELLIPSIS
, ranges
)) {
1121 /* We have reached the end, return the data from names. */
1122 g_string_free (names_with_sheet
, TRUE
);
1123 return g_string_free (names_with_ellipsis
, FALSE
);
1125 g_string_free (names_with_ellipsis
, TRUE
);
1128 /* Without the sheet name. */
1129 names
= g_string_new (NULL
);
1130 if (range_list_name_try (names
, NULL
, ranges
)) {
1131 /* We have reached the end, return the data from names. */
1132 if (names_with_sheet
!= NULL
)
1133 g_string_free (names_with_sheet
, TRUE
);
1134 return g_string_free (names
, FALSE
);
1137 /* We have to use a truncated version. */
1138 if (names_with_sheet
!= NULL
) {
1139 g_string_free (names
, TRUE
);
1140 return g_string_free (names_with_sheet
, FALSE
);
1142 return g_string_free (names
, FALSE
);
1146 * global_range_list_parse:
1147 * @sheet: Sheet where the range specification is relatively parsed to
1148 * @str: a range or list of ranges to parse (ex: "A1", "A1:B1,C2,Sheet2!D2:D4")
1150 * Parses a list of ranges, relative to the @sheet and returns a list with the
1153 * Returns: (element-type GnmValue) (transfer full): a #GSList of #GnmValue.
1156 global_range_list_parse (Sheet
*sheet
, char const *str
)
1159 GnmExprTop
const *texpr
;
1160 GSList
*ranges
= NULL
;
1163 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1164 g_return_val_if_fail (str
!= NULL
, NULL
);
1166 texpr
= gnm_expr_parse_str (str
,
1167 parse_pos_init_sheet (&pp
, sheet
),
1168 GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES
|
1169 GNM_EXPR_PARSE_PERMIT_MULTIPLE_EXPRESSIONS
|
1170 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS
,
1173 if (texpr
!= NULL
) {
1174 if (GNM_EXPR_GET_OPER (texpr
->expr
) == GNM_EXPR_OP_SET
) {
1175 GnmExpr
const *expr
= texpr
->expr
;
1177 for (i
= 0; i
< expr
->set
.argc
; i
++) {
1178 v
= gnm_expr_get_range (expr
->set
.argv
[i
]);
1180 range_list_destroy (ranges
);
1184 ranges
= g_slist_prepend (ranges
, v
);
1187 v
= gnm_expr_top_get_range (texpr
);
1189 ranges
= g_slist_prepend (ranges
, v
);
1191 gnm_expr_top_unref (texpr
);
1194 return g_slist_reverse (ranges
);
1198 * global_range_list_foreach:
1199 * @gr_list: (element-type GnmRange): list of ranges.
1202 * @handler: (scope call):
1203 * @closure: user data.
1205 * Returns: (transfer none):
1208 global_range_list_foreach (GSList
*gr_list
, GnmEvalPos
const *ep
,
1209 CellIterFlags flags
,
1210 CellIterFunc handler
,
1214 for (; gr_list
!= NULL
; gr_list
= gr_list
->next
) {
1215 v
= workbook_foreach_cell_in_range (ep
, gr_list
->data
,
1216 flags
, handler
, closure
);
1226 * global_range_contained:
1227 * @sheet: The calling context #Sheet for references with sheet==NULL
1231 * return true if a is contained in b
1232 * we do not handle 3d ranges
1235 global_range_contained (Sheet
const *sheet
, GnmValue
const *a
, GnmValue
const *b
)
1237 Sheet
const *target
;
1239 g_return_val_if_fail (a
!= NULL
, FALSE
);
1240 g_return_val_if_fail (b
!= NULL
, FALSE
);
1242 if (!VALUE_IS_CELLRANGE (a
) || !VALUE_IS_CELLRANGE (b
))
1245 target
= eval_sheet (a
->v_range
.cell
.a
.sheet
, sheet
);
1246 if (target
!= eval_sheet (a
->v_range
.cell
.b
.sheet
, sheet
))
1249 if (target
!= eval_sheet (b
->v_range
.cell
.a
.sheet
, sheet
) ||
1250 target
!= eval_sheet (b
->v_range
.cell
.b
.sheet
, sheet
))
1253 if (a
->v_range
.cell
.a
.row
< b
->v_range
.cell
.a
.row
)
1256 if (a
->v_range
.cell
.b
.row
> b
->v_range
.cell
.b
.row
)
1259 if (a
->v_range
.cell
.a
.col
< b
->v_range
.cell
.a
.col
)
1262 if (a
->v_range
.cell
.b
.col
> b
->v_range
.cell
.b
.col
)
1269 gnm_range_get_type (void)
1274 t
= g_boxed_type_register_static ("GnmRange",
1275 (GBoxedCopyFunc
)gnm_range_dup
,
1276 (GBoxedFreeFunc
)g_free
);