2 * expr-name.c: Supported named expressions
5 * Jody Goldberg <jody@gnome.org>
8 * Michael Meeks <michael@ximian.com>
11 #include <gnumeric-config.h>
12 #include <glib/gi18n-lib.h>
15 #include <expr-name.h>
17 #include <dependent.h>
19 #include <workbook-priv.h>
24 #include <sheet-style.h>
26 #include <goffice/goffice.h>
30 expr_name_validate_r1c1 (const char *name
)
35 if (p
[0] != 'R' && p
[0] != 'r')
38 /* no need to worry about [] since they are not alphanumeric */
39 for (i
= 0; p
[0] && g_ascii_isdigit (p
[0]); p
= g_utf8_next_char (p
))
43 if (p
[0] != 'C' && p
[0] != 'c')
46 for (i
= 0; p
[0] && g_ascii_isdigit (p
[0]); p
= g_utf8_next_char (p
))
50 return (p
[0] != '\0');
54 expr_name_validate_a1 (const char *name
)
59 for (i
= 0; *p
&& g_ascii_isalpha(p
[0]);
60 p
= g_utf8_next_char (p
))
62 if (i
==0 || i
>4) /* We want to allow "total2010" and it */
63 /* is unlikely to have more than 456976 */
66 for (i
= 0; *p
&& g_ascii_isdigit (p
[0]);
67 p
= g_utf8_next_char (p
))
76 * @name: tentative name
78 * returns %TRUE if the given name is valid, %FALSE otherwise.
81 expr_name_validate (const char *name
)
86 g_return_val_if_fail (name
!= NULL
, FALSE
);
91 v
= value_new_from_string (VALUE_BOOLEAN
, name
, NULL
, TRUE
);
93 v
= value_new_from_string (VALUE_BOOLEAN
, name
, NULL
, FALSE
);
99 /* Hmm... Now what? */
100 if (!g_unichar_isalpha (g_utf8_get_char (name
)) &&
104 for (p
= name
; *p
; p
= g_utf8_next_char (p
)) {
105 if (!g_unichar_isalnum (g_utf8_get_char (p
)) &&
110 /* Make sure it's not A1 etc.*/
111 /* Note that we can't use our regular parsers */
112 /* since we also have to avoid names that may become */
113 /* sensible when the sheet size changes. */
114 if (!expr_name_validate_a1 (name
))
117 /* What about R1C1? */
118 if (!expr_name_validate_r1c1 (name
))
126 cb_nexpr_remove (GnmNamedExpr
*nexpr
)
128 g_return_if_fail (nexpr
->scope
!= NULL
);
131 expr_name_set_expr (nexpr
, NULL
);
132 expr_name_unref (nexpr
);
136 cb_collect_name_deps (gpointer key
, G_GNUC_UNUSED gpointer value
,
139 GSList
**list
= user_data
;
140 *list
= g_slist_prepend (*list
, key
);
144 expr_name_unlink_deps (GnmNamedExpr
*nexpr
)
146 GSList
*ptr
, *deps
= NULL
;
148 if (nexpr
->dependents
== NULL
)
151 g_hash_table_foreach (nexpr
->dependents
, cb_collect_name_deps
, &deps
);
154 for (ptr
= deps
; ptr
!= NULL
; ptr
= ptr
->next
) {
155 GnmDependent
*dep
= ptr
->data
;
156 if (dependent_is_linked (dep
))
157 dependent_unlink (dep
);
163 expr_name_relink_deps (GnmNamedExpr
*nexpr
)
167 if (nexpr
->dependents
== NULL
)
170 g_hash_table_foreach (nexpr
->dependents
, cb_collect_name_deps
, &deps
);
171 dependents_link (deps
);
176 fake_go_string_hash (gconstpointer s_
)
178 const GOString
*s
= s_
;
179 return g_str_hash (s
->str
);
183 fake_go_string_equal (gconstpointer a_
, gconstpointer b_
)
185 const GOString
*a
= a_
;
186 const GOString
*b
= b_
;
187 return g_str_equal (a
->str
, b
->str
);
191 struct _GnmNamedExprCollection
{
192 /* all the defined names */
195 /* placeholders for references to undefined names */
196 GHashTable
*placeholders
;
199 unsigned ref_count
; /* boxed type */
203 * gnm_named_expr_collection_new:
205 * Returns: (transfer full): the newly allocated #GnmNamedExprCollection.
207 GnmNamedExprCollection
*
208 gnm_named_expr_collection_new (void)
210 GnmNamedExprCollection
*res
= g_new (GnmNamedExprCollection
, 1);
212 res
->names
= g_hash_table_new_full
213 (fake_go_string_hash
, fake_go_string_equal
,
214 NULL
, (GDestroyNotify
) cb_nexpr_remove
);
215 res
->placeholders
= g_hash_table_new_full
216 (fake_go_string_hash
, fake_go_string_equal
,
217 NULL
, (GDestroyNotify
) cb_nexpr_remove
);
224 * gnm_named_expr_collection_free:
225 * @names: The collection of names
227 * Frees names defined in the local scope.
228 * NOTE : THIS DOES NOT INVALIDATE NAMES THAT REFER
231 * in scope sheet2 we have a name that refers
232 * to sheet1. That will remain!
235 gnm_named_expr_collection_free (GnmNamedExprCollection
*names
)
237 if (names
!= NULL
&& names
->ref_count
-- < 2) {
238 g_hash_table_destroy (names
->names
);
239 g_hash_table_destroy (names
->placeholders
);
244 static GnmNamedExprCollection
*
245 gnm_named_expr_collection_ref (GnmNamedExprCollection
*names
)
252 gnm_named_expr_collection_dump (GnmNamedExprCollection
*names
, const char *id
)
254 g_printerr ("Named collection %s\n", id
);
256 g_printerr (" Empty\n");
260 if (names
->names
&& g_hash_table_size (names
->names
)) {
261 GHashTableIter hiter
;
264 g_printerr (" Defined names:\n");
265 g_hash_table_iter_init (&hiter
, names
->names
);
266 while (g_hash_table_iter_next (&hiter
, &key
, &value
)) {
267 const GOString
*name
= key
;
268 GnmNamedExpr
const *nexpr
= value
;
269 g_printerr (" [%s] =>\n", name
->str
);
270 if (name
!= nexpr
->name
)
271 g_printerr (" Weird keys: %p vs %p\n",
276 if (names
->placeholders
&& g_hash_table_size (names
->placeholders
)) {
277 GHashTableIter hiter
;
280 g_printerr (" Defined placeholders:\n");
281 g_hash_table_iter_init (&hiter
, names
->placeholders
);
282 while (g_hash_table_iter_next (&hiter
, &key
, &value
)) {
283 const GOString
*name
= key
;
284 GnmNamedExpr
const *nexpr
= value
;
285 g_printerr (" [%s] =>\n", name
->str
);
286 if (name
!= nexpr
->name
)
287 g_printerr (" Weird keys: %p vs %p\n",
294 gnm_named_expr_collection_sanity_check (GnmNamedExprCollection
*names
,
297 gboolean err
= FALSE
;
298 g_printerr ("Checking sanity for container %s\n", id
);
300 GHashTableIter hiter
;
303 g_hash_table_iter_init (&hiter
, names
->names
);
304 while (g_hash_table_iter_next (&hiter
, &key
, &value
)) {
305 const GOString
*name
= key
;
306 GnmNamedExpr
const *nexpr
= value
;
307 if (name
!= nexpr
->name
) {
309 g_printerr ("Container %s has strange defined name\n",
311 g_printerr (" key is %p [%s]\n",
313 g_printerr (" target's name is %p [%s]\n",
314 nexpr
->name
, nexpr
->name
->str
);
322 gnm_named_expr_collection_get_type (void)
327 t
= g_boxed_type_register_static ("GnmNamedExprCollection",
328 (GBoxedCopyFunc
)gnm_named_expr_collection_ref
,
329 (GBoxedFreeFunc
)gnm_named_expr_collection_free
);
335 cb_unlink_all_names (G_GNUC_UNUSED gpointer key
,
337 G_GNUC_UNUSED gpointer user_data
)
339 GnmNamedExpr
*nexpr
= value
;
340 GSList
*deps
= expr_name_unlink_deps (nexpr
);
345 gnm_named_expr_collection_unlink (GnmNamedExprCollection
*names
)
350 g_hash_table_foreach (names
->names
,
356 cb_relink_all_names (G_GNUC_UNUSED gpointer key
,
358 G_GNUC_UNUSED gpointer user_data
)
360 GnmNamedExpr
*nexpr
= value
;
361 expr_name_relink_deps (nexpr
);
365 gnm_named_expr_collection_relink (GnmNamedExprCollection
*names
)
370 g_hash_table_foreach (names
->names
,
376 gnm_named_expr_collection_lookup (GnmNamedExprCollection
const *scope
,
383 fake_name
.str
= name
;
384 nexpr
= g_hash_table_lookup (scope
->names
, &fake_name
);
386 nexpr
= g_hash_table_lookup (scope
->placeholders
,
394 cb_list_names (G_GNUC_UNUSED gpointer key
,
398 GSList
**pres
= user_data
;
399 GO_SLIST_PREPEND (*pres
, value
);
403 * gnm_named_expr_collection_list:
404 * @scope: #GnmNamedExprCollection
406 * Returns: (element-type GnmNamedExpr) (transfer container):
409 gnm_named_expr_collection_list (GnmNamedExprCollection
const *scope
)
413 g_hash_table_foreach (scope
->names
,
421 gnm_named_expr_collection_insert (GnmNamedExprCollection
*scope
,
424 if (gnm_debug_flag ("names")) {
425 char *scope_name
= nexpr
->pos
.sheet
426 ? g_strdup_printf ("sheet %s", nexpr
->pos
.sheet
->name_quoted
)
427 : g_strdup ("workbook");
428 g_printerr ("Inserting name %s into its %s container%s\n",
431 nexpr
->is_placeholder
? " as a placeholder" : "");
435 /* name can be active at this point, eg we are converting a
436 * placeholder, or changing a scope */
437 nexpr
->scope
= scope
;
439 (nexpr
->is_placeholder
? scope
->placeholders
: scope
->names
,
440 (gpointer
)nexpr
->name
, nexpr
);
450 cb_check_name (G_GNUC_UNUSED gpointer key
, GnmNamedExpr
*nexpr
,
455 if (nexpr
->scope
== NULL
|| nexpr
->is_hidden
|| !nexpr
->texpr
)
458 v
= gnm_expr_top_get_range (nexpr
->texpr
);
460 if (VALUE_IS_CELLRANGE (v
)) {
461 GnmRangeRef
const *ref
= &v
->v_range
.cell
;
462 if (!ref
->a
.col_relative
&&
463 !ref
->b
.col_relative
&&
464 !ref
->a
.row_relative
&&
465 !ref
->b
.row_relative
&&
466 eval_sheet (ref
->a
.sheet
, user
->sheet
) == user
->sheet
&&
467 eval_sheet (ref
->b
.sheet
, user
->sheet
) == user
->sheet
&&
468 MIN (ref
->a
.col
, ref
->b
.col
) == user
->r
->start
.col
&&
469 MAX (ref
->a
.col
, ref
->b
.col
) == user
->r
->end
.col
&&
470 MIN (ref
->a
.row
, ref
->b
.row
) == user
->r
->start
.row
&&
471 MAX (ref
->a
.row
, ref
->b
.row
) == user
->r
->end
.row
)
478 static GnmNamedExpr
*
479 gnm_named_expr_collection_check (GnmNamedExprCollection
*scope
,
480 Sheet
const *sheet
, GnmRange
const *r
)
491 g_hash_table_foreach (scope
->names
, (GHFunc
)cb_check_name
, &user
);
496 * gnm_named_expr_collection_foreach:
497 * @names: #GnmNamedExprCollection
498 * @func: (scope call):
501 * Iterate over all names, including placeholders.
504 gnm_named_expr_collection_foreach (GnmNamedExprCollection
*names
,
508 g_hash_table_foreach (names
->names
, func
, data
);
509 g_hash_table_foreach (names
->placeholders
, func
, data
);
512 /******************************************************************************/
515 * expr_name_handle_references:
517 * Register or unregister a name with
518 * all of the sheets it explicitly references. This is necessary
519 * because names are not dependents, and if they reference a deleted
520 * sheet we will not notice.
523 expr_name_handle_references (GnmNamedExpr
*nexpr
, gboolean add
)
525 GSList
*sheets
, *ptr
;
527 sheets
= gnm_expr_top_referenced_sheets (nexpr
->texpr
);
529 for (ptr
= sheets
; ptr
!= NULL
; ptr
= ptr
->next
) {
530 Sheet
*sheet
= ptr
->data
;
533 /* Implicit reference. */
537 /* No need to do anything during destruction */
538 if (sheet
->deps
== NULL
)
541 found
= g_hash_table_lookup (sheet
->deps
->referencing_names
, nexpr
);
544 g_hash_table_insert (sheet
->deps
->referencing_names
, nexpr
, nexpr
);
546 g_warning ("Name being registered multiple times ?");
550 g_warning ("Unregistered name being removed?");
552 g_hash_table_remove (sheet
->deps
->referencing_names
, nexpr
);
556 g_slist_free (sheets
);
562 * @pos: #GnmParsePos identifying a #Sheet or a #Workbook.
563 * @name: name of #GnmNamedExpr to look up.
565 * Return: (transfer none) (nullable): #GnmNamedExpr named @name in the scope
566 * given by @pos; %NULL if no such #GnmNamedExpr exists.
569 expr_name_lookup (GnmParsePos
const *pp
, char const *name
)
571 GnmNamedExpr
*res
= NULL
;
572 Sheet
const *sheet
= NULL
;
573 Workbook
const *wb
= NULL
;
575 g_return_val_if_fail (name
!= NULL
, NULL
);
579 wb
= (sheet
!= NULL
) ? sheet
->workbook
: pp
->wb
;
582 if (sheet
!= NULL
&& sheet
->names
!= NULL
)
583 res
= gnm_named_expr_collection_lookup (sheet
->names
, name
);
584 if (res
== NULL
&& wb
!= NULL
&& wb
->names
!= NULL
)
585 res
= gnm_named_expr_collection_lookup (wb
->names
, name
);
593 * Creates a new name without linking it into any container.
596 expr_name_new (char const *name
)
600 g_return_val_if_fail (name
!= NULL
, NULL
);
602 nexpr
= g_new0 (GnmNamedExpr
,1);
604 nexpr
->ref_count
= 1;
605 nexpr
->name
= go_string_new (name
);
607 nexpr
->dependents
= NULL
;
608 nexpr
->is_placeholder
= TRUE
;
609 nexpr
->is_hidden
= FALSE
;
610 nexpr
->is_permanent
= FALSE
;
611 nexpr
->is_editable
= TRUE
;
614 if (gnm_debug_flag ("names"))
615 g_printerr ("Created new name %s\n", name
);
620 struct cb_name_loop_check
{
625 gboolean stop_at_name
;
629 static GnmExpr
const *
630 cb_name_loop_check (GnmExpr
const *expr
, GnmExprWalk
*data
)
632 struct cb_name_loop_check
*args
= data
->user
;
634 GnmNamedExpr
const *nexpr2
= gnm_expr_get_name (expr
);
636 if ((args
->name
&& !strcmp (nexpr2
->name
->str
, args
->name
)) ||
637 args
->nexpr
== nexpr2
||
638 (!args
->stop_at_name
&& nexpr2
->texpr
&&
639 /* Is the following right? It drops args->nexpr */
640 expr_name_check_for_loop (args
->name
, nexpr2
->texpr
))) {
650 * expr_name_check_for_loop:
651 * @name: tentative name
652 * @texpr: tentative expression
654 * Returns: %TRUE if defining the tentative name would cause a circular
657 * NOTE: if we already have a circular reference beforehand, we will come
661 expr_name_check_for_loop (char const *name
, GnmExprTop
const *texpr
)
663 struct cb_name_loop_check args
;
665 g_return_val_if_fail (texpr
!= NULL
, TRUE
);
669 args
.stop_at_name
= FALSE
;
671 gnm_expr_walk (texpr
->expr
, cb_name_loop_check
, &args
);
676 expr_name_queue_deps (GnmNamedExpr
*nexpr
)
678 if (nexpr
->dependents
)
679 g_hash_table_foreach (nexpr
->dependents
,
680 (GHFunc
)dependent_queue_recalc
,
685 * expr_name_add: (skip)
688 * @texpr: if texpr == NULL then create a placeholder with value #NAME?
689 * @error_msg: (out) (optional) (nullable):
690 * @link_to_container:
692 * Absorbs the reference to @texpr.
693 * If @error_msg is non NULL it may hold a pointer to a translated descriptive
694 * string. NOTE : caller is responsible for freeing the error message.
696 * The reference semantics of the new expression are
697 * 1) new names with @link_to_container TRUE are referenced by the container.
698 * The caller DOES NOT OWN a reference to the result, and needs to add their
700 * 2) if @link_to_container is %FALSE the caller DOES OWN a reference, and
701 * can free the result by unrefing the name.
704 expr_name_add (GnmParsePos
const *pp
, char const *name
,
705 GnmExprTop
const *texpr
, char **error_msg
,
706 gboolean link_to_container
,
709 GnmNamedExpr
*nexpr
= NULL
;
710 GnmNamedExprCollection
*scope
= NULL
;
713 g_return_val_if_fail (pp
!= NULL
, NULL
);
714 g_return_val_if_fail (pp
->sheet
!= NULL
|| pp
->wb
!= NULL
, NULL
);
715 g_return_val_if_fail (name
!= NULL
, NULL
);
716 g_return_val_if_fail (stub
== NULL
|| stub
->is_placeholder
, NULL
);
721 fake_name
.str
= name
;
723 if (gnm_debug_flag ("names")) {
724 char *str
= gnm_expr_top_as_string (texpr
, pp
, NULL
);
725 g_printerr ("Adding Name=[%s] texpr=[%s] stub=[%s]\n",
726 name
, str
, stub
? expr_name_name (stub
) : "-");
731 (expr_name_check_for_loop (name
, texpr
) ||
732 (stub
&& expr_name_check_for_loop (expr_name_name (stub
), texpr
)))) {
733 gnm_expr_top_unref (texpr
);
735 *error_msg
= g_strdup_printf (_("'%s' has a circular reference"), name
);
739 scope
= (pp
->sheet
!= NULL
) ? pp
->sheet
->names
: pp
->wb
->names
;
740 /* see if there was a place holder */
741 nexpr
= g_hash_table_lookup (scope
->placeholders
, &fake_name
);
744 /* there was already a placeholder for this */
745 if (!link_to_container
)
746 expr_name_ref (nexpr
);
750 /* convert the placeholder into a real name */
751 g_hash_table_steal (scope
->placeholders
, &fake_name
);
752 nexpr
->is_placeholder
= FALSE
;
754 nexpr
= g_hash_table_lookup (scope
->names
, &fake_name
);
755 /* If this is a permanent name, we may be adding it */
756 /* on opening of a file, although */
757 /* the name is already in place. */
759 if (nexpr
->is_permanent
)
760 link_to_container
= FALSE
;
762 if (error_msg
!= NULL
)
763 *error_msg
= (pp
->sheet
!= NULL
)
764 ? g_strdup_printf (_("'%s' is already defined in sheet"), name
)
765 : g_strdup_printf (_("'%s' is already defined in workbook"), name
);
767 gnm_expr_top_unref (texpr
);
776 stub
->is_placeholder
= FALSE
;
777 go_string_unref (stub
->name
);
778 stub
->name
= go_string_new (name
);
780 nexpr
= expr_name_new (name
);
781 nexpr
->is_placeholder
= (texpr
== NULL
);
784 parse_pos_init (&nexpr
->pos
,
785 pp
->wb
, pp
->sheet
, pp
->eval
.col
, pp
->eval
.row
);
787 texpr
= gnm_expr_top_new_constant
788 (value_new_error_NAME (NULL
));
789 expr_name_set_expr (nexpr
, texpr
);
790 if (link_to_container
)
791 gnm_named_expr_collection_insert (scope
, nexpr
);
797 expr_name_ref (GnmNamedExpr
*nexpr
)
799 g_return_val_if_fail (nexpr
!= NULL
, NULL
);
805 expr_name_unref (GnmNamedExpr
*nexpr
)
807 g_return_if_fail (nexpr
!= NULL
);
809 if (nexpr
->ref_count
-- > 1)
812 if (gnm_debug_flag ("names"))
813 g_printerr ("Finalizing name %s\n", nexpr
->name
->str
);
815 g_return_if_fail (nexpr
->scope
== NULL
);
817 go_string_unref (nexpr
->name
);
820 if (nexpr
->texpr
!= NULL
)
821 expr_name_set_expr (nexpr
, NULL
);
823 if (nexpr
->dependents
!= NULL
) {
824 g_hash_table_destroy (nexpr
->dependents
);
825 nexpr
->dependents
= NULL
;
828 nexpr
->pos
.wb
= NULL
;
829 nexpr
->pos
.sheet
= NULL
;
835 gnm_named_expr_get_type (void)
840 t
= g_boxed_type_register_static ("GnmNamedExpr",
841 (GBoxedCopyFunc
)expr_name_ref
,
842 (GBoxedFreeFunc
)expr_name_unref
);
851 * Remove a @nexpr from its container and deactivate it.
852 * NOTE : @nexpr may continue to exist if things still have references to it,
853 * but they will evaluate to #REF!
856 expr_name_remove (GnmNamedExpr
*nexpr
)
858 g_return_if_fail (nexpr
!= NULL
);
859 g_return_if_fail (nexpr
->scope
!= NULL
);
861 if (gnm_debug_flag ("names")) {
862 g_printerr ("Removing name %s from its container%s\n",
864 nexpr
->is_placeholder
? " as a placeholder" : "");
867 g_hash_table_remove (
868 nexpr
->is_placeholder
? nexpr
->scope
->placeholders
: nexpr
->scope
->names
,
873 expr_name_name (GnmNamedExpr
const *nexpr
)
875 g_return_val_if_fail (nexpr
!= NULL
, NULL
);
876 return nexpr
->name
->str
;
880 * expr_name_set_name:
881 * @nexpr: the named expression
882 * @new_name: the new name of the expression
884 * returns: TRUE on error.
887 expr_name_set_name (GnmNamedExpr
*nexpr
,
888 const char *new_name
)
890 const char *old_name
;
892 GOString fake_new_name
;
894 g_return_val_if_fail (nexpr
!= NULL
, TRUE
);
895 g_return_val_if_fail (nexpr
->scope
== NULL
|| new_name
, TRUE
);
897 old_name
= nexpr
->name
->str
;
898 if (go_str_compare (new_name
, old_name
) == 0)
901 fake_new_name
.str
= new_name
;
903 g_printerr ("Renaming %s to %s\n", old_name
, new_name
);
906 ? (nexpr
->is_placeholder
907 ? nexpr
->scope
->placeholders
908 : nexpr
->scope
->names
)
912 (g_hash_table_lookup (nexpr
->scope
->placeholders
,
914 g_hash_table_lookup (nexpr
->scope
->names
,
916 /* The only error not to be blamed on the programmer is
921 g_hash_table_steal (h
, nexpr
->name
);
924 go_string_unref (nexpr
->name
);
925 nexpr
->name
= go_string_new (new_name
);
928 g_hash_table_insert (h
, (gpointer
)nexpr
->name
, nexpr
);
935 * expr_name_as_string:
936 * @nexpr: A #GnmNamedExpr
937 * @pp: (nullable): Position where name was defined.
938 * @fmt: #GnmConventions describing how to render @nexpr
940 * Returns: (transfer full): The rendering of @nexpr given convention @fmt.
943 expr_name_as_string (GnmNamedExpr
const *nexpr
, GnmParsePos
const *pp
,
944 GnmConventions
const *fmt
)
948 return gnm_expr_top_as_string (nexpr
->texpr
, pp
, fmt
);
953 * @nexpr: A #GnmNamedExpr
954 * @pos: Position where evaluation takes place
955 * @flags: #GnmExprEvalFlags flags describing context.
957 * Returns: (transfer full): The resulting value.
960 expr_name_eval (GnmNamedExpr
const *nexpr
, GnmEvalPos
const *pos
,
961 GnmExprEvalFlags flags
)
963 g_return_val_if_fail (pos
, NULL
);
966 return value_new_error_NAME (pos
);
968 return gnm_expr_top_eval (nexpr
->texpr
, pos
, flags
);
972 * expr_name_downgrade_to_placeholder:
975 * Takes a real non-placeholder name and converts it to being a placeholder.
976 * unrefing its expression
979 expr_name_downgrade_to_placeholder (GnmNamedExpr
*nexpr
)
981 g_return_if_fail (nexpr
!= NULL
);
983 expr_name_set_is_placeholder (nexpr
, TRUE
);
986 gnm_expr_top_new_constant (value_new_error_NAME (NULL
)));
989 /*******************************************************************
990 * Manage things that depend on named expressions.
994 * @nexpr: the named expression
995 * @pp: the new position
997 * Returns a translated error string which the caller must free if something
1001 expr_name_set_pos (GnmNamedExpr
*nexpr
, GnmParsePos
const *pp
)
1003 GnmNamedExprCollection
*old_scope
, *new_scope
;
1005 g_return_val_if_fail (nexpr
!= NULL
, NULL
);
1006 g_return_val_if_fail (pp
!= NULL
, NULL
);
1008 old_scope
= nexpr
->scope
;
1009 new_scope
= pp
->sheet
? pp
->sheet
->names
: pp
->wb
->names
;
1011 if (old_scope
!= new_scope
&&
1012 (g_hash_table_lookup (new_scope
->placeholders
, nexpr
->name
) ||
1013 g_hash_table_lookup (new_scope
->names
, nexpr
->name
))) {
1014 const char *fmt
= pp
->sheet
1015 ? _("'%s' is already defined in sheet")
1016 : _("'%s' is already defined in workbook");
1017 return g_strdup_printf (fmt
, nexpr
->name
);
1022 (nexpr
->is_placeholder
? old_scope
->placeholders
: old_scope
->names
,
1026 gnm_named_expr_collection_insert (new_scope
, nexpr
);
1031 * expr_name_set_expr:
1032 * @nexpr: #GnmNamedExpr to change
1033 * @texpr: (transfer full) (nullable): the new contents
1035 * Set the content of @nexpr to @texpr.
1038 expr_name_set_expr (GnmNamedExpr
*nexpr
, GnmExprTop
const *texpr
)
1040 GSList
*good
= NULL
;
1042 g_return_if_fail (nexpr
!= NULL
);
1044 if (texpr
== nexpr
->texpr
)
1046 if (nexpr
->texpr
!= NULL
) {
1047 GSList
*deps
= NULL
, *junk
= NULL
;
1049 deps
= expr_name_unlink_deps (nexpr
);
1050 expr_name_handle_references (nexpr
, FALSE
);
1051 gnm_expr_top_unref (nexpr
->texpr
);
1054 * We do not want to relink deps for sheets that are going
1055 * away. This speeds up exit for workbooks with lots of
1059 GSList
*next
= deps
->next
;
1060 GnmDependent
*dep
= deps
->data
;
1062 if (dep
->sheet
&& dep
->sheet
->being_invalidated
)
1063 deps
->next
= junk
, junk
= deps
;
1065 deps
->next
= good
, good
= deps
;
1070 g_slist_free (junk
);
1072 nexpr
->texpr
= texpr
;
1073 dependents_link (good
);
1074 g_slist_free (good
);
1077 expr_name_handle_references (nexpr
, TRUE
);
1079 expr_name_queue_deps (nexpr
);
1083 expr_name_add_dep (GnmNamedExpr
*nexpr
, GnmDependent
*dep
)
1085 if (nexpr
->dependents
== NULL
)
1086 nexpr
->dependents
= g_hash_table_new (g_direct_hash
,
1089 g_hash_table_insert (nexpr
->dependents
, dep
, dep
);
1093 expr_name_remove_dep (GnmNamedExpr
*nexpr
, GnmDependent
*dep
)
1095 g_return_if_fail (nexpr
->dependents
!= NULL
);
1097 g_hash_table_remove (nexpr
->dependents
, dep
);
1101 * expr_name_is_placeholder:
1104 * Returns: %TRUE if @ne is a placeholder for an unknown name
1107 expr_name_is_placeholder (GnmNamedExpr
const *nexpr
)
1109 g_return_val_if_fail (nexpr
!= NULL
, FALSE
);
1111 return (nexpr
->texpr
&&
1112 gnm_expr_top_is_err (nexpr
->texpr
, GNM_ERROR_NAME
));
1116 expr_name_set_is_placeholder (GnmNamedExpr
*nexpr
, gboolean is_placeholder
)
1118 g_return_if_fail (nexpr
!= NULL
);
1120 is_placeholder
= !!is_placeholder
;
1121 if (nexpr
->is_placeholder
== is_placeholder
)
1123 nexpr
->is_placeholder
= is_placeholder
;
1126 g_hash_table_steal (is_placeholder
1127 ? nexpr
->scope
->names
1128 : nexpr
->scope
->placeholders
,
1130 gnm_named_expr_collection_insert (nexpr
->scope
, nexpr
);
1135 expr_name_is_active (GnmNamedExpr
const *nexpr
)
1137 g_return_val_if_fail (nexpr
!= NULL
, FALSE
);
1138 return nexpr
->scope
!= NULL
;
1141 struct cb_expr_name_in_use
{
1142 GnmNamedExpr
*nexpr
;
1147 cb_expr_name_in_use (G_GNUC_UNUSED gconstpointer key
,
1148 GnmNamedExpr
*nexpr
,
1149 struct cb_expr_name_in_use
*pdata
)
1151 if (!pdata
->in_use
) {
1152 struct cb_name_loop_check args
;
1155 args
.nexpr
= pdata
->nexpr
;
1156 args
.stop_at_name
= TRUE
;
1158 gnm_expr_walk (nexpr
->texpr
->expr
, cb_name_loop_check
, &args
);
1159 pdata
->in_use
= args
.res
;
1165 * @nexpr: A named expression.
1167 * Returns: TRUE, if the named expression appears to be in use. This is an
1168 * approximation only, as we only look at the workbook in which the name is
1173 expr_name_in_use (GnmNamedExpr
*nexpr
)
1176 struct cb_expr_name_in_use data
;
1178 if (nexpr
->dependents
!= NULL
&&
1179 g_hash_table_size (nexpr
->dependents
) != 0)
1183 data
.in_use
= FALSE
;
1185 wb
= nexpr
->pos
.sheet
? nexpr
->pos
.sheet
->workbook
: nexpr
->pos
.wb
;
1186 workbook_foreach_name (wb
, FALSE
,
1187 (GHFunc
)cb_expr_name_in_use
,
1195 expr_name_cmp_by_name (GnmNamedExpr
const *a
, GnmNamedExpr
const *b
)
1197 Sheet
const *sheeta
= a
->pos
.sheet
;
1198 Sheet
const *sheetb
= b
->pos
.sheet
;
1201 if (sheeta
!= sheetb
) {
1202 /* Locals after non-locals. */
1203 if (!sheeta
|| !sheetb
)
1204 return (!sheeta
) - (!sheetb
);
1206 /* By non-local sheet order. */
1207 res
= g_utf8_collate (sheeta
->name_case_insensitive
,
1208 sheetb
->name_case_insensitive
);
1211 if (res
== 0) /* By name. */
1212 res
= go_utf8_collate_casefold (a
->name
->str
, b
->name
->str
);
1218 * sheet_names_check:
1222 * Returns: (transfer none) (nullable): The name of a #GnmNamedExpr if
1223 * @sheet!@r is the target of a named range.
1225 * Preference is given to workbook scope over sheet.
1228 sheet_names_check (Sheet
const *sheet
, GnmRange
const *r
)
1230 GnmNamedExpr
*nexpr
;
1233 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1234 g_return_val_if_fail (r
!= NULL
, NULL
);
1237 range_normalize (&tmp
);
1238 nexpr
= gnm_named_expr_collection_check (sheet
->names
, sheet
, &tmp
);
1239 if (nexpr
== NULL
) {
1240 nexpr
= gnm_named_expr_collection_check (sheet
->workbook
->names
, sheet
, &tmp
);
1241 /* The global name is not accessible if there is a local name (#306685) */
1242 if (nexpr
!= NULL
&&
1243 gnm_named_expr_collection_lookup (sheet
->names
, nexpr
->name
->str
) != NULL
)
1247 return (nexpr
!= NULL
) ? nexpr
->name
->str
: NULL
;
1252 * expr_name_perm_add:
1254 * @texpr: string to be the value of the name
1255 * @is_editable: whether this is a predefined action
1257 * This is a wrapper around expr_name_add to set this as permanent name.
1262 expr_name_perm_add (Sheet
*sheet
, char const *name
,
1263 GnmExprTop
const *value
,
1264 gboolean is_editable
)
1269 parse_pos_init_sheet (&pp
, sheet
);
1270 res
= expr_name_add (&pp
, name
, value
, NULL
, TRUE
, NULL
);
1272 res
->is_permanent
= TRUE
;
1273 res
->is_editable
= is_editable
;
1277 /* ------------------------------------------------------------------------- */
1280 expr_name_set_expr_ref (GnmNamedExpr
*nexpr
, GnmExprTop
const *texpr
)
1282 gnm_expr_top_ref (texpr
);
1283 expr_name_set_expr (nexpr
, texpr
);
1287 * expr_name_set_expr_undo_new:
1288 * @nexpr: #GnmNamedExpr
1290 * Returns: (transfer full):
1293 expr_name_set_expr_undo_new (GnmNamedExpr
*ne
)
1296 gnm_expr_top_ref (ne
->texpr
);
1298 return go_undo_binary_new (ne
, (gpointer
)ne
->texpr
,
1299 (GOUndoBinaryFunc
)expr_name_set_expr_ref
,
1300 (GFreeFunc
)expr_name_unref
,
1301 (GFreeFunc
)gnm_expr_top_unref
);
1304 /* ------------------------------------------------------------------------- */