2 * ranges.c: various functions for common operations on cell ranges.
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)
10 #include <gnumeric-config.h>
17 #include <expr-impl.h>
18 #include <expr-name.h>
20 #include <sheet-style.h>
21 #include <parse-util.h>
26 #include <gnumeric-conf.h>
30 #include <glib/gi18n-lib.h>
34 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
37 * range_init_full_sheet:
41 * Updates @r to fill @sheet in its entirety.
43 * Returns: (skip) (transfer none): @r
46 range_init_full_sheet (GnmRange
*r
, Sheet
const *sheet
)
50 r
->end
.col
= gnm_sheet_get_last_col (sheet
);
51 r
->end
.row
= gnm_sheet_get_last_row (sheet
);
59 * @start_col: Starting column
60 * @end_col: Ending column
62 * Updates @r to span full columns @start_col through @end_col completely.
64 * Returns: (skip) (transfer none): @r
67 range_init_cols (GnmRange
*r
, Sheet
const *sheet
, int start_col
, int end_col
)
69 r
->start
.col
= start_col
;
72 r
->end
.row
= gnm_sheet_get_last_row (sheet
);
80 * @start_row: Starting row
81 * @end_row: Ending row
83 * Updates @r to span full rows @start_row through @end_row completely.
85 * Returns: (skip) (transfer none): @r
88 range_init_rows (GnmRange
*r
, Sheet
const *sheet
, int start_row
, int end_row
)
91 r
->start
.row
= start_row
;
92 r
->end
.col
= gnm_sheet_get_last_col (sheet
);
98 * range_init_invalid: (skip)
101 * Updates @r to a meaningless range
103 * Returns: (skip) (transfer none): @r
106 range_init_invalid (GnmRange
*r
)
116 * range_init_rangeref:
120 * Updates @r to be the same as the range part of @rr.
122 * Returns: (skip) (transfer none): @r
125 range_init_rangeref (GnmRange
*range
, GnmRangeRef
const *rr
)
127 g_return_val_if_fail (range
!= NULL
&& rr
!= NULL
, NULL
);
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
;
139 * @r: A #GnmRange to change
140 * @v: A #GnmValue containing a cell range
142 * Updates @r to be the same as the cell range of @v.
144 * Returns: (skip) (transfer none): @r
147 range_init_value (GnmRange
*range
, GnmValue
const *v
)
149 g_return_val_if_fail (range
!= NULL
, NULL
);
150 g_return_val_if_fail (v
!= NULL
&& VALUE_IS_CELLRANGE (v
), NULL
);
152 return range_init_rangeref (range
, &v
->v_range
.cell
);
156 * range_init_cellpos:
157 * @r: A #GnmRange to change
158 * @pos: A #GnmCellPos
160 * Updates @r to be the singleton range of @pos
162 * Returns: (skip) (transfer none): @r
165 range_init_cellpos (GnmRange
*r
, GnmCellPos
const *pos
)
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
180 * Updates @r to start at @start and spanning @cols columns and @rows rows.
182 * Returns: (skip) (transfer none): @r
185 range_init_cellpos_size (GnmRange
*r
,
186 GnmCellPos
const *start
, int cols
, int rows
)
189 r
->end
.col
= start
->col
+ cols
- 1;
190 r
->end
.row
= start
->row
+ rows
- 1;
197 * @r: A #GnmRange to change
203 * Updates @r to start at (@start_col,@start_row) and end
204 * at (@end_col,@end_row).
206 * Returns: (skip) (transfer none): @r
209 range_init (GnmRange
*r
, int start_col
, int start_row
,
210 int end_col
, int end_row
)
212 g_return_val_if_fail (r
!= NULL
, r
);
214 r
->start
.col
= start_col
;
215 r
->start
.row
= start_row
;
216 r
->end
.col
= end_col
;
217 r
->end
.row
= end_row
;
225 * @text: text to parse
226 * @ss: #GnmSheetSize describing the size of the sheet in which @r lives.
228 * Parse a simple range (no abs/rel refs, no sheet refs)
229 * Store the result in @r.
231 * Returns: %TRUE on success.
234 range_parse (GnmRange
*r
, char const *text
, GnmSheetSize
const *ss
)
236 text
= cellpos_parse (text
, ss
, &r
->start
, FALSE
);
248 text
= cellpos_parse (text
+ 1, ss
, &r
->end
, TRUE
);
253 * range_list_destroy:
254 * @ranges: (element-type GnmValue) (transfer full): a list of value ranges
257 * Destroys a list of ranges returned from parse_cell_range_list. Note:
258 * the element type here is GnmValue, not GnmRange.
261 range_list_destroy (GSList
*ranges
)
263 g_slist_free_full (ranges
, (GDestroyNotify
)value_release
);
271 * Returns: (transfer none): a string representation of @src
274 range_as_string (GnmRange
const *r
)
276 static char buffer
[(6 + 4 * sizeof (long)) * 2 + 1];
278 g_return_val_if_fail (r
!= NULL
, "");
280 sprintf (buffer
, "%s%s",
281 col_name (r
->start
.col
), row_name (r
->start
.row
));
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
));
291 range_dump (GnmRange
const *src
, char const *suffix
)
294 * keep these as 2 print statements, because
295 * col_name and row_name use a static buffer
298 col_name (src
->start
.col
),
299 row_name (src
->start
.row
));
301 if (src
->start
.col
!= src
->end
.col
||
302 src
->start
.row
!= src
->end
.row
)
304 col_name (src
->end
.col
),
305 row_name (src
->end
.row
));
306 g_printerr ("%s", suffix
);
311 ranges_dump (GList
*l
, char const *txt
)
313 g_printerr ("%s: ", txt
);
314 for (; l
; l
= l
->next
) {
315 range_dump (l
->data
, "");
328 * Is @a totally contained by @b
333 range_contained (GnmRange
const *a
, GnmRange
const *b
)
335 if (a
->start
.row
< b
->start
.row
)
338 if (a
->end
.row
> b
->end
.row
)
341 if (a
->start
.col
< b
->start
.col
)
344 if (a
->end
.col
> b
->end
.col
)
351 * range_split_ranges:
352 * @hard: The region that is split against
353 * @soft: The region that is split
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).
359 * Returns: (element-type GnmRange) (transfer full): A list of fragments.
362 range_split_ranges (GnmRange
const *hard
, GnmRange
const *soft
)
365 * There are lots of cases so think carefully.
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.
372 * NB. We prefer to have long columns at the expense
375 GSList
*split
= NULL
;
376 GnmRange
*middle
, *sp
;
377 gboolean split_left
= FALSE
;
378 gboolean split_right
= FALSE
;
380 g_return_val_if_fail (range_overlap (hard
, soft
), NULL
);
382 middle
= g_new (GnmRange
, 1);
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
);
394 middle
->start
.col
= hard
->start
.col
;
396 } /* else shared edge */
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
);
407 middle
->end
.col
= hard
->end
.col
;
409 } /* else shared edge */
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
);
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
);
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
);
446 middle
->start
.row
= hard
->start
.row
;
447 } /* else shared edge */
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
);
458 middle
->start
.row
= hard
->start
.row
;
459 } /* else shared edge */
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
);
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
);
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
);
497 middle
->end
.row
= hard
->end
.row
;
498 } /* else shared edge */
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
);
509 middle
->end
.row
= hard
->end
.row
;
510 } /* else shared edge */
513 return g_slist_prepend (split
, middle
);
518 * @r: Source range to copy
520 * Copies the @r range.
522 * Returns: (transfer full): A copy of the GnmRange.
525 gnm_range_dup (GnmRange
const *r
)
527 return g_memdup (r
, sizeof (*r
));
532 * @a: First #GnmRange
533 * @b: Second #GnmRange
535 * Fragments the ranges into totally non-overlapping regions,
537 * Returns: (element-type GnmRange) (transfer full): A list of fragmented
538 * ranges or at minimum simply @a and @b.
541 range_fragment (GnmRange
const *a
, GnmRange
const *b
)
543 GSList
*split
, *ans
= NULL
;
545 split
= range_split_ranges (a
, b
);
546 ans
= g_slist_concat (ans
, split
);
548 split
= range_split_ranges (b
, a
);
550 g_free (split
->data
);
551 split
= g_slist_remove (split
, split
->data
);
553 ans
= g_slist_concat (ans
, split
);
559 * range_intersection:
560 * @r: intersection range
561 * @a: First #GnmRange
562 * @b: Second #GnmRange
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.
569 * NB. totally commutative
571 * Return value: %TRUE if the ranges intersect, %FALSE otherwise
574 range_intersection (GnmRange
*r
, GnmRange
const *a
, GnmRange
const *b
)
576 if (!range_overlap (a
, b
)) {
577 *r
= *a
; // Something
581 r
->start
.col
= MAX (a
->start
.col
, b
->start
.col
);
582 r
->start
.row
= MAX (a
->start
.row
, b
->start
.row
);
584 r
->end
.col
= MIN (a
->end
.col
, b
->end
.col
);
585 r
->end
.row
= MIN (a
->end
.row
, b
->end
.row
);
594 * Ensures that start <= end for rows and cols.
597 range_normalize (GnmRange
*src
)
602 if (src
->start
.col
> tmp
) {
603 src
->end
.col
= src
->start
.col
;
604 src
->start
.col
= tmp
;
607 if (src
->start
.row
> tmp
) {
608 src
->end
.row
= src
->start
.row
;
609 src
->start
.row
= tmp
;
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.
624 * Return value: the union
627 range_union (GnmRange
const *a
, GnmRange
const *b
)
631 if (a
->start
.col
< b
->start
.col
)
632 ans
.start
.col
= a
->start
.col
;
634 ans
.start
.col
= b
->start
.col
;
636 if (a
->end
.col
> b
->end
.col
)
637 ans
.end
.col
= a
->end
.col
;
639 ans
.end
.col
= b
->end
.col
;
641 if (a
->start
.row
< b
->start
.row
)
642 ans
.start
.row
= a
->start
.row
;
644 ans
.start
.row
= b
->start
.row
;
646 if (a
->end
.row
> b
->end
.row
)
647 ans
.end
.row
= a
->end
.row
;
649 ans
.end
.row
= b
->end
.row
;
655 * range_is_singleton:
658 * Returns: %TRUE if @r is a single-cell range.
661 range_is_singleton (GnmRange
const *r
)
663 return r
->start
.col
== r
->end
.col
&& r
->start
.row
== r
->end
.row
;
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_).
673 * This determines whether @r completely spans a sheet in the dimension
674 * specified by @horiz.
676 * Returns: %TRUE if it is infinite, %FALSE otherwise.
679 range_is_full (GnmRange
const *r
, Sheet
const *sheet
, gboolean horiz
)
682 return (r
->start
.col
<= 0 &&
683 r
->end
.col
>= gnm_sheet_get_last_col (sheet
));
685 return (r
->start
.row
<= 0 &&
686 r
->end
.row
>= gnm_sheet_get_last_row (sheet
));
690 * range_clip_to_finite:
692 * @sheet: the sheet in which @range lives
694 * Clip the range to the area of the sheet with content.
695 * if the range reaches the edge
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.
702 * WARNING THIS IS EXPENSIVE!
705 range_clip_to_finite (GnmRange
*range
, Sheet
*sheet
)
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
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
;
724 * Returns: width of @r.
727 range_width (GnmRange
const *r
)
729 g_return_val_if_fail (r
!= NULL
, 0);
730 return ABS (r
->end
.col
- r
->start
.col
) + 1;
737 * Returns: height of @r.
740 range_height (GnmRange
const *r
)
742 g_return_val_if_fail (r
!= NULL
, 0);
743 return ABS (r
->end
.row
- r
->start
.row
) + 1;
749 * @sheet: the sheet in which @range lives
753 * Translate the range and return %TRUE if it is invalidated.
755 * Return: %TRUE if the range is no longer valid.
758 range_translate (GnmRange
*range
, Sheet
const *sheet
, int col_offset
, int row_offset
)
761 * FIXME: we should probably check for overflow without actually
764 range
->start
.col
+= col_offset
;
765 range
->end
.col
+= col_offset
;
766 range
->start
.row
+= row_offset
;
767 range
->end
.row
+= row_offset
;
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)
780 * range_ensure_sanity:
781 * @range: the range to check
782 * @sheet: the sheet in which @range lives
784 * Silently clip a range to ensure that it does not contain areas
785 * outside the valid bounds. Does NOT fix inverted ranges.
788 range_ensure_sanity (GnmRange
*range
, Sheet
const *sheet
)
790 range
->start
.col
= MAX (0, range
->start
.col
);
791 range
->end
.col
= MIN (range
->end
.col
, gnm_sheet_get_last_col (sheet
));
793 range
->start
.row
= MAX (0, range
->start
.row
);
794 range
->end
.row
= MIN (range
->end
.row
, gnm_sheet_get_last_row (sheet
));
799 * @range: the range to check
801 * Generate warnings if the range is out of bounds or inverted.
804 range_is_sane (GnmRange
const *range
)
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
);
820 * @sheet: the sheet in which @range lives
821 * @origin: The box to transpose inside
823 * Effectively mirrors the ranges in 'boundary' around a
824 * leading diagonal projected from offset.
826 * Return value: whether we clipped the range.
829 range_transpose (GnmRange
*range
, Sheet
const *sheet
, GnmCellPos
const *origin
)
831 gboolean clipped
= FALSE
;
834 int last_col
= gnm_sheet_get_last_col (sheet
);
835 int last_row
= gnm_sheet_get_last_row (sheet
);
837 g_return_val_if_fail (range
!= NULL
, TRUE
);
842 t
= origin
->col
+ (src
.start
.row
- origin
->row
);
845 range
->start
.col
= last_col
;
848 range
->start
.col
= 0;
850 range
->start
.col
= t
;
853 t
= origin
->row
+ (src
.start
.col
- origin
->col
);
856 range
->start
.row
= last_row
;
859 range
->start
.row
= 0;
861 range
->start
.row
= t
;
865 t
= origin
->col
+ (src
.end
.row
- origin
->row
);
868 range
->end
.col
= last_col
;
876 t
= origin
->row
+ (src
.end
.col
- origin
->col
);
879 range
->end
.row
= last_row
;
886 g_assert (range_valid (range
));
892 gnm_range_equal (const GnmRange
*a
, const GnmRange
*b
)
894 return range_equal (a
, b
);
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.
908 gnm_range_compare (GnmRange
const *a
, GnmRange
const *b
)
911 if (!i
) i
= a
->start
.row
- b
->start
.row
;
912 if (!i
) i
= a
->start
.col
- b
->start
.col
;
913 if (!i
) i
= a
->end
.row
- b
->end
.row
;
914 if (!i
) i
= a
->end
.col
- b
->end
.col
;
920 gnm_sheet_range_new (Sheet
*sheet
, GnmRange
const *r
)
924 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
925 g_return_val_if_fail (r
!= NULL
, NULL
);
927 gr
= g_new0 (GnmSheetRange
, 1);
935 gnm_sheet_range_dup (GnmSheetRange
const *sr
)
937 g_return_val_if_fail (sr
!= NULL
, NULL
);
938 return gnm_sheet_range_new (sr
->sheet
, &sr
->range
);
942 * gnm_sheet_range_from_value:
943 * @r: #GnmSheetRange to change
944 * @v: #GnmValue containing a cell range.
946 * Convert @v into a GnmSheetRange.
951 gnm_sheet_range_from_value (GnmSheetRange
*r
, GnmValue
const *v
)
953 g_return_val_if_fail (VALUE_IS_CELLRANGE (v
), FALSE
);
955 r
->sheet
= v
->v_range
.cell
.a
.sheet
;
956 range_init_value (&r
->range
, v
);
962 gnm_sheet_range_free (GnmSheetRange
*gr
)
968 gnm_sheet_range_get_type (void)
973 t
= g_boxed_type_register_static ("GnmSheetRange",
974 (GBoxedCopyFunc
)gnm_sheet_range_dup
,
975 (GBoxedFreeFunc
)gnm_sheet_range_free
);
980 gnm_sheet_range_equal (const GnmSheetRange
*a
,
981 const GnmSheetRange
*b
)
983 return a
->sheet
== b
->sheet
&& range_equal (&a
->range
, &b
->range
);
987 gnm_sheet_range_overlap (GnmSheetRange
const *a
, GnmSheetRange
const *b
)
989 g_return_val_if_fail (a
!= NULL
, FALSE
);
990 g_return_val_if_fail (b
!= NULL
, FALSE
);
992 if (a
->sheet
== b
->sheet
&& range_overlap (&a
->range
, &b
->range
))
999 global_range_name (Sheet
const *sheet
, GnmRange
const *r
)
1001 char const * the_range_name
= range_as_string (r
);
1004 return g_strdup (the_range_name
);
1006 return g_strdup_printf ("%s!%s", sheet
->name_quoted
, the_range_name
);
1010 * undo_cell_pos_name:
1014 * Returns the range name depending on the preference setting.
1017 undo_cell_pos_name (Sheet
const *sheet
, GnmCellPos
const *pos
)
1020 r
.end
= r
.start
= *pos
;
1021 return undo_range_name (sheet
, &r
);
1029 * Returns the range name depending on the preference setting.
1032 undo_range_name (Sheet
const *sheet
, GnmRange
const *r
)
1034 char const *the_range_name
= range_as_string (r
);
1036 if (sheet
!= NULL
&& gnm_conf_get_undo_show_sheet_name ()) {
1037 GString
*str
= g_string_new (NULL
);
1038 gboolean truncated
= FALSE
;
1040 g_string_printf (str
, "%s!%s", sheet
->name_quoted
, the_range_name
);
1041 gnm_cmd_trunc_descriptor (str
, &truncated
);
1044 return g_string_free (str
, FALSE
);
1046 g_string_printf (str
, UNICODE_ELLIPSIS
"!%s", the_range_name
);
1047 gnm_cmd_trunc_descriptor (str
, &truncated
);
1050 return g_string_free (str
, FALSE
);
1051 g_string_free (str
, TRUE
);
1054 return g_string_free
1055 (gnm_cmd_trunc_descriptor (g_string_new (the_range_name
), NULL
), FALSE
);
1060 * Create range list name, but don't exceed max_width.
1061 * Returns: %TRUE iff the name is complete.
1064 range_list_name_try (GString
*names
, char const *sheet
, GSList
const *ranges
)
1067 char const *n
= range_as_string (ranges
->data
);
1071 g_string_assign (names
, n
);
1073 g_string_printf (names
, "%s!%s", sheet
, n
);
1075 gnm_cmd_trunc_descriptor (names
, &truncated
);
1080 for (l
= ranges
->next
; l
!= NULL
; l
= l
->next
) {
1081 n
= range_as_string (l
->data
);
1084 g_string_append_printf (names
, ", %s", n
);
1086 g_string_append_printf (names
, ", %s!%s",
1089 gnm_cmd_trunc_descriptor (names
, &truncated
);
1095 /* Have we reached the end? */
1101 * undo_range_list_name:
1103 * @ranges: (element-type GnmRange): list of ranges
1105 * Returns: the range list name depending on the preference setting.
1106 * (The result will be something like: "A1:C3, D4:E5"). The string will be
1107 * truncated to max_descriptor_width.
1110 undo_range_list_name (Sheet
const *sheet
, GSList
const *ranges
)
1112 GString
*names_with_sheet
= NULL
, *names_with_ellipsis
, *names
;
1114 g_return_val_if_fail (ranges
!= NULL
, NULL
);
1116 /* With the sheet name. */
1117 if (sheet
!= NULL
&& gnm_conf_get_undo_show_sheet_name ()) {
1118 names_with_sheet
= g_string_new (NULL
);
1119 if (range_list_name_try (names_with_sheet
, sheet
->name_quoted
, ranges
)) {
1120 /* We have reached the end, return the data from names. */
1121 return g_string_free (names_with_sheet
, FALSE
);
1123 names_with_ellipsis
= g_string_new (NULL
);
1124 if (range_list_name_try (names_with_ellipsis
, UNICODE_ELLIPSIS
, ranges
)) {
1125 /* We have reached the end, return the data from names. */
1126 g_string_free (names_with_sheet
, TRUE
);
1127 return g_string_free (names_with_ellipsis
, FALSE
);
1129 g_string_free (names_with_ellipsis
, TRUE
);
1132 /* Without the sheet name. */
1133 names
= g_string_new (NULL
);
1134 if (range_list_name_try (names
, NULL
, ranges
)) {
1135 /* We have reached the end, return the data from names. */
1136 if (names_with_sheet
!= NULL
)
1137 g_string_free (names_with_sheet
, TRUE
);
1138 return g_string_free (names
, FALSE
);
1141 /* We have to use a truncated version. */
1142 if (names_with_sheet
!= NULL
) {
1143 g_string_free (names
, TRUE
);
1144 return g_string_free (names_with_sheet
, FALSE
);
1146 return g_string_free (names
, FALSE
);
1150 * global_range_list_parse:
1151 * @sheet: Sheet where the range specification is relatively parsed to
1152 * @str: a range or list of ranges to parse (ex: "A1", "A1:B1,C2,Sheet2!D2:D4")
1154 * Parses a list of ranges, relative to the @sheet and returns a list with the
1157 * Returns: (element-type GnmValue) (transfer full): a #GSList of #GnmValue.
1160 global_range_list_parse (Sheet
*sheet
, char const *str
)
1163 GnmExprTop
const *texpr
;
1164 GSList
*ranges
= NULL
;
1167 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1168 g_return_val_if_fail (str
!= NULL
, NULL
);
1170 texpr
= gnm_expr_parse_str (str
,
1171 parse_pos_init_sheet (&pp
, sheet
),
1172 GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES
|
1173 GNM_EXPR_PARSE_PERMIT_MULTIPLE_EXPRESSIONS
|
1174 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS
,
1177 if (texpr
!= NULL
) {
1178 if (GNM_EXPR_GET_OPER (texpr
->expr
) == GNM_EXPR_OP_SET
) {
1179 GnmExpr
const *expr
= texpr
->expr
;
1181 for (i
= 0; i
< expr
->set
.argc
; i
++) {
1182 v
= gnm_expr_get_range (expr
->set
.argv
[i
]);
1184 range_list_destroy (ranges
);
1188 ranges
= g_slist_prepend (ranges
, v
);
1191 v
= gnm_expr_top_get_range (texpr
);
1193 ranges
= g_slist_prepend (ranges
, v
);
1195 gnm_expr_top_unref (texpr
);
1198 return g_slist_reverse (ranges
);
1202 * global_range_list_foreach:
1203 * @gr_list: (element-type GnmRange): list of ranges.
1206 * @handler: (scope call):
1207 * @closure: user data.
1209 * Returns: (transfer none):
1212 global_range_list_foreach (GSList
*gr_list
, GnmEvalPos
const *ep
,
1213 CellIterFlags flags
,
1214 CellIterFunc handler
,
1218 for (; gr_list
!= NULL
; gr_list
= gr_list
->next
) {
1219 v
= workbook_foreach_cell_in_range (ep
, gr_list
->data
,
1220 flags
, handler
, closure
);
1230 * global_range_contained:
1231 * @sheet: The calling context #Sheet for references without sheet.
1232 * @a: A #GnmValue representing a range
1233 * @b: A #GnmValue representing a range
1235 * Returns: %TRUE if @a is contained in @b. We do not handle 3d ranges
1238 global_range_contained (Sheet
const *sheet
, GnmValue
const *a
, GnmValue
const *b
)
1240 Sheet
const *target
;
1242 g_return_val_if_fail (a
!= NULL
, FALSE
);
1243 g_return_val_if_fail (b
!= NULL
, FALSE
);
1245 if (!VALUE_IS_CELLRANGE (a
) || !VALUE_IS_CELLRANGE (b
))
1248 target
= eval_sheet (a
->v_range
.cell
.a
.sheet
, sheet
);
1249 if (target
!= eval_sheet (a
->v_range
.cell
.b
.sheet
, sheet
))
1252 if (target
!= eval_sheet (b
->v_range
.cell
.a
.sheet
, sheet
) ||
1253 target
!= eval_sheet (b
->v_range
.cell
.b
.sheet
, sheet
))
1256 if (a
->v_range
.cell
.a
.row
< b
->v_range
.cell
.a
.row
)
1259 if (a
->v_range
.cell
.b
.row
> b
->v_range
.cell
.b
.row
)
1262 if (a
->v_range
.cell
.a
.col
< b
->v_range
.cell
.a
.col
)
1265 if (a
->v_range
.cell
.b
.col
> b
->v_range
.cell
.b
.col
)
1272 gnm_range_get_type (void)
1277 t
= g_boxed_type_register_static ("GnmRange",
1278 (GBoxedCopyFunc
)gnm_range_dup
,
1279 (GBoxedFreeFunc
)g_free
);