2 * dependent.c: Manage calculation dependencies between objects
4 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
5 * Copyright (C) 2006-2013 Morten Welinder (terra@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 #include <gnumeric-config.h>
24 #include <dependent.h>
26 #include <workbook-priv.h>
31 #include <expr-impl.h>
32 #include <expr-name.h>
33 #include <application.h>
34 #include <workbook-view.h>
37 #include <sheet-view.h>
40 #include <goffice/goffice.h>
43 static gboolean
gnm_cell_eval_content (GnmCell
*cell
);
44 static void dependent_changed (GnmDependent
*dep
);
45 static void dependent_clear_dynamic_deps (GnmDependent
*dep
);
47 /* ------------------------------------------------------------------------- */
50 * Note: we unconditionally use pools for
53 * since we need the ability to free en masse.
61 static GOMemChunk
*micro_few_pool
;
62 static GOMemChunk
*cset_pool
;
63 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
64 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
65 #define MICRO_HASH_FEW 3 /* Odd and small. */
66 #define NEW_FEW CHUNK_ALLOC (gpointer, micro_few_pool)
67 #define FREE_FEW(p) CHUNK_FREE (micro_few_pool, p)
69 #define CHUNK_ALLOC(T,c) g_slice_new (T)
70 #define CHUNK_FREE(p,v) g_slice_free1 (sizeof(*v),(v))
71 #define MICRO_HASH_FEW 4 /* Even and small. */
72 #define NEW_FEW (gpointer *)g_slice_alloc (MICRO_HASH_FEW * sizeof (gpointer))
73 #define FREE_FEW(p) g_slice_free1 (MICRO_HASH_FEW * sizeof (gpointer), p)
76 /* ------------------------------------------------------------------------- */
77 /* Maps between row numbers and bucket numbers. */
79 #define BUCKET_SIZE 1024
80 #define BUCKET_OF_ROW(row) ((row) / BUCKET_SIZE)
81 #define BUCKET_START_ROW(b) ((b) * BUCKET_SIZE)
82 #define BUCKET_END_ROW(b) ((b) * BUCKET_SIZE + (BUCKET_SIZE - 1))
84 /* ------------------------------------------------------------------------- */
87 #define CSET_SEGMENT_SIZE 29
89 typedef struct _CSet CSet
;
93 gpointer data
[CSET_SEGMENT_SIZE
];
94 /* And one pointer for allocation overhead. */
99 cset_find (CSet
*list
, gpointer datum
)
102 guint i
= list
->count
;
104 if (list
->data
[i
] == datum
)
113 cset_free (CSet
*list
)
116 CSet
*next
= list
->next
;
117 CHUNK_FREE (cset_pool
, list
);
122 /* NOTE: takes reference. */
124 cset_insert (CSet
**list
, gpointer datum
)
127 if (cs
== NULL
|| cs
->count
== CSET_SEGMENT_SIZE
) {
128 CSet
*h
= *list
= CHUNK_ALLOC (CSet
, cset_pool
);
133 cs
->data
[cs
->count
++] = datum
;
136 /* NOTE: takes reference. Returns %TRUE if datum was already present. */
138 cset_insert_checked (CSet
**list
, gpointer datum
)
141 CSet
*nonfull
= NULL
;
145 if (i
!= CSET_SEGMENT_SIZE
)
148 if (cs
->data
[i
] == datum
)
154 nonfull
->data
[nonfull
->count
++] = datum
;
156 cset_insert (list
, datum
);
161 /* NOTE: takes reference. Returns %TRUE if removed. */
163 cset_remove (CSet
**list
, gpointer datum
)
165 CSet
*l
, *last
= NULL
;
167 for (l
= *list
; l
; l
= l
->next
) {
170 for (i
= l
->count
; i
-- > 0; )
171 if (l
->data
[i
] == datum
) {
175 last
->next
= l
->next
;
178 CHUNK_FREE (cset_pool
, l
);
180 l
->data
[i
] = l
->data
[l
->count
];
188 #define CSET_FOREACH(list,var,code) \
191 for (cs_ = (list); cs_; cs_ = cs_->next) { \
193 for (i_ = cs_->count; i_-- > 0; ) { \
194 var = cs_->data[i_]; \
201 /* ------------------------------------------------------------------------- */
204 gnm_dep_set_expr_undo_undo (GnmDependent
*dep
, GnmExprTop
const *texpr
)
206 dependent_set_expr (dep
, texpr
);
207 dependent_link (dep
);
208 dependent_changed (dep
);
212 gnm_dep_set_expr_undo_new (GnmDependent
*dep
)
214 gnm_expr_top_ref (dep
->texpr
);
215 return go_undo_binary_new (dep
, (gpointer
)dep
->texpr
,
216 (GOUndoBinaryFunc
)gnm_dep_set_expr_undo_undo
,
218 (GFreeFunc
)gnm_expr_top_unref
);
222 gnm_dep_unlink_undo_new (GSList
*deps
)
224 return go_undo_unary_new (deps
,
225 (GOUndoUnaryFunc
)dependents_link
,
226 (GFreeFunc
)g_slist_free
);
229 /* ------------------------------------------------------------------------- */
231 #undef DEBUG_EVALUATION
234 dummy_dep_eval (G_GNUC_UNUSED GnmDependent
*dep
)
238 static void cell_dep_eval (GnmDependent
*dep
);
239 static void cell_dep_set_expr (GnmDependent
*dep
, GnmExprTop
const *new_texpr
);
240 static GSList
*cell_dep_changed (GnmDependent
*dep
);
241 static GnmCellPos
*cell_dep_pos (GnmDependent
const *dep
);
242 static void cell_dep_debug_name (GnmDependent
const *dep
, GString
*target
);
243 static const GnmDependentClass cell_dep_class
= {
251 static GSList
*dynamic_dep_changed (GnmDependent
*dep
);
252 static void dynamic_dep_debug_name (GnmDependent
const *dep
, GString
*target
);
253 static const GnmDependentClass dynamic_dep_class
= {
258 dynamic_dep_debug_name
,
262 GnmDependent
*container
;
267 static void name_dep_debug_name (GnmDependent
const *dep
, GString
*target
);
268 static const GnmDependentClass name_dep_class
= {
276 static void managed_dep_debug_name (GnmDependent
const *dep
, GString
*target
);
277 static const GnmDependentClass managed_dep_class
= {
282 managed_dep_debug_name
,
285 static void style_dep_eval (GnmDependent
*dep
);
286 static GSList
*style_dep_changed (GnmDependent
*dep
);
287 static GnmCellPos
*style_dep_pos (GnmDependent
const *dep
);
288 static void style_dep_debug_name (GnmDependent
const *dep
, GString
*target
);
289 static const GnmDependentClass style_dep_class
= {
294 style_dep_debug_name
,
302 static GPtrArray
*dep_classes
= NULL
;
305 dependent_types_init (void)
307 g_return_if_fail (dep_classes
== NULL
);
309 /* Init with a NULL class so we can access directly */
310 dep_classes
= g_ptr_array_new ();
311 g_ptr_array_add (dep_classes
, NULL
); /* bogus filler */
312 g_ptr_array_add (dep_classes
, (gpointer
)&cell_dep_class
);
313 g_ptr_array_add (dep_classes
, (gpointer
)&dynamic_dep_class
);
314 g_ptr_array_add (dep_classes
, (gpointer
)&name_dep_class
);
315 g_ptr_array_add (dep_classes
, (gpointer
)&managed_dep_class
);
316 g_ptr_array_add (dep_classes
, (gpointer
)&style_dep_class
);
320 go_mem_chunk_new ("micro few pool",
321 MICRO_HASH_FEW
* sizeof (gpointer
),
324 go_mem_chunk_new ("cset pool",
331 dependent_types_shutdown (void)
333 g_return_if_fail (dep_classes
!= NULL
);
334 g_ptr_array_free (dep_classes
, TRUE
);
338 go_mem_chunk_destroy (micro_few_pool
, FALSE
);
339 micro_few_pool
= NULL
;
340 go_mem_chunk_destroy (cset_pool
, FALSE
);
346 * dependent_register_type:
349 * Store the vtable and allocate an ID for a new class
353 dependent_type_register (GnmDependentClass
const *klass
)
357 g_return_val_if_fail (dep_classes
!= NULL
, 0);
359 g_ptr_array_add (dep_classes
, (gpointer
)klass
);
360 res
= dep_classes
->len
-1;
362 g_return_val_if_fail (res
<= DEPENDENT_TYPE_MASK
, res
);
368 * dependent_flag_recalc:
369 * @dep: the dependent that contains the expression needing recomputation.
371 * Marks @dep as needing recalculation
372 * NOTE : it does NOT recursively dirty dependencies.
374 #define dependent_flag_recalc(dep) \
375 do { (dep)->flags |= DEPENDENT_NEEDS_RECALC; } while (0)
379 * @cell: the dependent that changed
384 dependent_changed (GnmDependent
*dep
)
387 dep
->sheet
->workbook
->recursive_dirty_enabled
)
388 dependent_queue_recalc (dep
);
390 dependent_flag_recalc (dep
);
394 * dependent_set_expr:
395 * @dep: The dependent we are interested in.
396 * @new_texpr: new expression.
398 * When the expression associated with @dep needs to change this routine
399 * dispatches to the virtual handler, unlinking @dep if necessary beforehand.
400 * Adds a ref to @new_expr.
402 * NOTE : it does NOT relink dependents in case they are going to move later.
405 dependent_set_expr (GnmDependent
*dep
, GnmExprTop
const *new_texpr
)
407 int const t
= dependent_type (dep
);
408 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
410 if (dependent_is_linked (dep
))
411 dependent_unlink (dep
);
412 if (dep
->flags
& DEPENDENT_HAS_DYNAMIC_DEPS
)
413 dependent_clear_dynamic_deps (dep
);
416 klass
->set_expr (dep
, new_texpr
);
419 gnm_expr_top_ref (new_texpr
);
421 gnm_expr_top_unref (dep
->texpr
);
422 dep
->texpr
= new_texpr
;
424 dependent_changed (dep
);
429 dependent_has_pos (GnmDependent
const *dep
)
431 int const t
= dependent_type (dep
);
432 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
433 return klass
->pos
!= NULL
;
437 dependent_pos (GnmDependent
const *dep
)
439 static GnmCellPos
const dummy
= { 0, 0 };
440 int const t
= dependent_type (dep
);
441 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
443 return klass
->pos
? klass
->pos (dep
) : &dummy
;
447 dependent_move (GnmDependent
*dep
, int dx
, int dy
)
449 int const t
= dependent_type (dep
);
450 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
453 g_return_if_fail (klass
->pos
!= NULL
);
454 pos
= klass
->pos (dep
);
455 /* Need a virtual for this? */
462 * dependent_set_sheet:
467 dependent_set_sheet (GnmDependent
*dep
, Sheet
*sheet
)
469 g_return_if_fail (dep
!= NULL
);
470 g_return_if_fail (dep
->sheet
== NULL
);
471 g_return_if_fail (!dependent_is_linked (dep
));
475 dependent_link (dep
);
476 dependent_changed (dep
);
481 cb_cell_list_deps (GnmDependent
*dep
, gpointer user
)
483 GSList
**list
= (GSList
**)user
;
484 *list
= g_slist_prepend (*list
, dep
);
488 cell_list_deps (GnmCell
const *cell
)
491 cell_foreach_dep (cell
, cb_cell_list_deps
, &deps
);
496 dependent_queue_recalc_main (GSList
*work
)
499 * Work is now a list of marked cells whose dependencies need
500 * to be marked. Marking early guarentees that we will not
501 * get duplicates. (And it thus limits the length of the list.)
502 * We treat work as a stack.
506 GnmDependent
*dep
= work
->data
;
507 int const t
= dependent_type (dep
);
508 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
510 /* Pop the top element. */
511 work
= g_slist_delete_link (work
, work
);
513 if (klass
->changed
) {
514 GSList
*extra
= klass
->changed (dep
);
516 g_slist_last (extra
)->next
= work
;
525 * dependent_queue_recalc_list:
528 * Queues any elements of @list for recalc that are not already queued,
529 * and marks all elements as needing a recalc.
532 dependent_queue_recalc_list (GSList
*list
)
536 for (; list
!= NULL
; list
= list
->next
) {
537 GnmDependent
*dep
= list
->data
;
538 if (!dependent_needs_recalc (dep
)) {
539 dependent_flag_recalc (dep
);
540 work
= g_slist_prepend (work
, dep
);
544 dependent_queue_recalc_main (work
);
548 dependent_queue_recalc (GnmDependent
*dep
)
550 g_return_if_fail (dep
!= NULL
);
552 #ifdef DEBUG_EVALUATION
553 g_printerr ("/* QUEUE (%s) */\n", cell_name (GNM_DEP_TO_CELL (dep
)));
555 if (!dependent_needs_recalc (dep
)) {
559 dependent_queue_recalc_list (&listrec
);
563 /**************************************************************************/
575 #define MICRO_HASH_MIN_SIZE 11
576 #define MICRO_HASH_MAX_SIZE 13845163
578 #define MICRO_HASH_hash(key) ((guint)GPOINTER_TO_UINT(key))
581 micro_hash_many_to_few (MicroHash
*hash_table
)
583 CSet
**buckets
= hash_table
->u
.many
;
584 int nbuckets
= hash_table
->num_buckets
;
587 hash_table
->u
.few
= NEW_FEW
;
589 while (nbuckets
-- > 0 ) {
592 CSET_FOREACH (buckets
[nbuckets
], datum
, {
593 hash_table
->u
.few
[i
++] = datum
;
595 cset_free (buckets
[nbuckets
]);
602 micro_hash_many_resize (MicroHash
*hash_table
, int new_nbuckets
)
604 CSet
**buckets
= hash_table
->u
.many
;
605 int nbuckets
= hash_table
->num_buckets
;
606 CSet
**new_buckets
= g_new0 (CSet
*, new_nbuckets
);
608 hash_table
->u
.many
= new_buckets
;
609 hash_table
->num_buckets
= new_nbuckets
;
611 while (nbuckets
-- > 0 ) {
614 CSET_FOREACH (buckets
[nbuckets
], datum
, {
615 guint bucket
= MICRO_HASH_hash (datum
) % new_nbuckets
;
616 cset_insert (&(new_buckets
[bucket
]), datum
);
618 cset_free (buckets
[nbuckets
]);
625 int capacity
= 0, totlen
= 0;
628 for (i
= 0; i
< new_nbuckets
; i
++) {
629 CSet
*cs
= new_buckets
[i
];
634 capacity
+= CSET_SEGMENT_SIZE
;
640 g_printerr ("resize %p: %d [%d %.1f %.0f%%]\n",
643 hash_table
->num_elements
,
644 (double)totlen
/ nonzero
,
645 100.0 * totlen
/ capacity
);
652 micro_hash_few_to_many (MicroHash
*hash_table
)
654 int nbuckets
= hash_table
->num_buckets
= MICRO_HASH_MIN_SIZE
;
655 CSet
**buckets
= g_new0 (CSet
*, nbuckets
);
658 for (i
= 0; i
< hash_table
->num_elements
; i
++) {
659 gpointer datum
= hash_table
->u
.few
[i
];
660 guint bucket
= MICRO_HASH_hash (datum
) % nbuckets
;
661 cset_insert (&(buckets
[bucket
]), datum
);
663 FREE_FEW (hash_table
->u
.few
);
664 hash_table
->u
.many
= buckets
;
670 micro_hash_insert (MicroHash
*hash_table
, gpointer key
)
672 int N
= hash_table
->num_elements
;
674 g_return_if_fail (key
!= NULL
);
677 hash_table
->u
.one
= key
;
679 gpointer key0
= hash_table
->u
.one
;
683 hash_table
->u
.few
= NEW_FEW
;
684 hash_table
->u
.few
[0] = key0
;
685 hash_table
->u
.few
[1] = key
;
686 memset (hash_table
->u
.few
+ 2, 0, (MICRO_HASH_FEW
- 2) * sizeof (gpointer
));
687 } else if (N
<= MICRO_HASH_FEW
) {
690 for (i
= 0; i
< N
; i
++)
691 if (hash_table
->u
.few
[i
] == key
)
694 if (N
== MICRO_HASH_FEW
) {
697 micro_hash_few_to_many (hash_table
);
698 bucket
= MICRO_HASH_hash (key
) % hash_table
->num_buckets
;
699 cset_insert (&(hash_table
->u
.many
[bucket
]), key
);
701 hash_table
->u
.few
[N
] = key
;
703 int nbuckets
= hash_table
->num_buckets
;
704 guint bucket
= MICRO_HASH_hash (key
) % nbuckets
;
705 CSet
**buckets
= hash_table
->u
.many
;
707 if (cset_insert_checked (&(buckets
[bucket
]), key
))
710 if (N
> CSET_SEGMENT_SIZE
* nbuckets
&&
711 nbuckets
< MICRO_HASH_MAX_SIZE
) {
712 int new_nbuckets
= g_spaced_primes_closest (N
/ (CSET_SEGMENT_SIZE
/ 2));
713 if (new_nbuckets
> MICRO_HASH_MAX_SIZE
)
714 new_nbuckets
= MICRO_HASH_MAX_SIZE
;
715 micro_hash_many_resize (hash_table
, new_nbuckets
);
719 hash_table
->num_elements
++;
723 micro_hash_remove (MicroHash
*hash_table
, gpointer key
)
725 int N
= hash_table
->num_elements
;
732 if (hash_table
->u
.one
!= key
)
734 hash_table
->u
.one
= NULL
;
735 hash_table
->num_elements
--;
739 if (N
<= MICRO_HASH_FEW
) {
742 for (i
= 0; i
< N
; i
++)
743 if (hash_table
->u
.few
[i
] == key
) {
744 hash_table
->u
.few
[i
] = hash_table
->u
.few
[N
- 1];
745 hash_table
->num_elements
--;
746 if (hash_table
->num_elements
> 1)
749 key
= hash_table
->u
.few
[0];
750 FREE_FEW (hash_table
->u
.few
);
751 hash_table
->u
.one
= key
;
757 bucket
= MICRO_HASH_hash (key
) % hash_table
->num_buckets
;
758 if (cset_remove (&(hash_table
->u
.many
[bucket
]), key
)) {
759 hash_table
->num_elements
--;
761 if (hash_table
->num_elements
<= MICRO_HASH_FEW
)
762 micro_hash_many_to_few (hash_table
);
772 micro_hash_release (MicroHash
*hash_table
)
774 int N
= hash_table
->num_elements
;
778 else if (N
<= MICRO_HASH_FEW
)
779 FREE_FEW (hash_table
->u
.few
);
781 guint i
= hash_table
->num_buckets
;
783 cset_free (hash_table
->u
.many
[i
]);
784 g_free (hash_table
->u
.many
);
786 hash_table
->num_elements
= 0;
787 hash_table
->num_buckets
= 1;
788 hash_table
->u
.one
= NULL
;
792 micro_hash_init (MicroHash
*hash_table
, gpointer key
)
794 hash_table
->num_elements
= 1;
795 hash_table
->u
.one
= key
;
798 static inline gboolean
799 micro_hash_is_empty (MicroHash
const *hash_table
)
801 return hash_table
->num_elements
== 0;
804 /*************************************************************************/
806 #define micro_hash_foreach_dep(dc, dep, code) do { \
807 guint i_ = dc.num_elements; \
808 if (i_ <= MICRO_HASH_FEW) { \
809 const gpointer *e_ = (i_ == 1) ? &dc.u.one : dc.u.few; \
811 GnmDependent *dep = e_[i_]; \
816 guint b_ = dc.num_buckets; \
818 CSET_FOREACH (dc.u.many[b_], dep, code); \
822 /**************************************************************************
823 * Data structures for managing dependencies between objects.
825 * The DependencyRange hash needs to be improved. It is a huge
826 * performance hit when there are large numbers of range depends.
830 * A DependencyRange defines a range of cells whose values
831 * are used by another objects in the spreadsheet.
833 * A change in those cells will trigger a recomputation on the
834 * cells listed in deps.
837 MicroHash deps
; /* Must be first */
842 * A DependencySingle stores a list of dependents that rely
843 * on the cell at @pos.
845 * A change in this cell will trigger a recomputation on the
846 * cells listed in deps.
849 MicroHash deps
; /* Must be first */
855 MicroHash deps
; /* Must be first */
859 deprange_hash (DependencyRange
const *r
)
861 guint a
= r
->range
.start
.row
;
862 guint b
= r
->range
.end
.row
;
863 guint c
= r
->range
.start
.col
;
864 guint d
= r
->range
.end
.col
;
866 return (((((a
<< 8) + b
) << 8) + c
) << 8) + d
;
870 deprange_equal (DependencyRange
const *r1
, DependencyRange
const *r2
)
872 return range_equal (&(r1
->range
), &(r2
->range
));
876 depsingle_hash (DependencySingle
const *depsingle
)
878 return (depsingle
->pos
.row
<< 8) ^ depsingle
->pos
.col
;
882 depsingle_equal (DependencySingle
const *a
, DependencySingle
const *b
)
884 return (a
->pos
.row
== b
->pos
.row
&& a
->pos
.col
== b
->pos
.col
);
887 static GnmDependentFlags
888 link_single_dep (GnmDependent
*dep
, GnmCellPos
const *pos
, GnmCellRef
const *ref
)
890 DependencySingle lookup
;
891 DependencySingle
*single
;
892 GnmDependentFlags flag
= DEPENDENT_NO_FLAG
;
893 Sheet
const *sheet
= eval_sheet (ref
->sheet
, dep
->sheet
);
894 GnmDepContainer
*deps
= sheet
->deps
;
896 if (sheet
!= dep
->sheet
)
897 flag
= (sheet
->workbook
!= dep
->sheet
->workbook
)
898 ? DEPENDENT_GOES_INTERBOOK
899 : DEPENDENT_GOES_INTERSHEET
;
901 /* Inserts if it is not already there */
902 gnm_cellpos_init_cellref (&lookup
.pos
, ref
, pos
, sheet
);
903 single
= g_hash_table_lookup (deps
->single_hash
, &lookup
);
904 if (single
== NULL
) {
905 single
= go_mem_chunk_alloc (deps
->single_pool
);
907 micro_hash_init (&single
->deps
, dep
);
908 g_hash_table_insert (deps
->single_hash
, single
, single
);
910 micro_hash_insert (&single
->deps
, dep
);
915 static GnmDependentFlags
916 unlink_single_dep (GnmDependent
*dep
, GnmCellPos
const *pos
, GnmCellRef
const *a
)
918 DependencySingle lookup
;
919 DependencySingle
*single
;
920 GnmDependentFlags flag
= DEPENDENT_NO_FLAG
;
921 Sheet
const *sheet
= eval_sheet (a
->sheet
, dep
->sheet
);
922 GnmDepContainer
*deps
= sheet
->deps
;
924 if (sheet
!= dep
->sheet
)
925 flag
= (sheet
->workbook
!= dep
->sheet
->workbook
)
926 ? DEPENDENT_GOES_INTERBOOK
927 : DEPENDENT_GOES_INTERSHEET
;
931 gnm_cellpos_init_cellref (&lookup
.pos
, a
, pos
, sheet
);
932 single
= g_hash_table_lookup (deps
->single_hash
, &lookup
);
933 if (single
!= NULL
) {
934 micro_hash_remove (&single
->deps
, dep
);
935 if (micro_hash_is_empty (&single
->deps
)) {
936 g_hash_table_remove (deps
->single_hash
, single
);
937 micro_hash_release (&single
->deps
);
938 go_mem_chunk_free (deps
->single_pool
, single
);
945 static inline GnmDependentFlags
946 link_unlink_single_dep (GnmDependent
*dep
, GnmCellPos
const *pos
,
947 GnmCellRef
const *a
, gboolean qlink
)
950 ? link_single_dep (dep
, pos
, a
)
951 : unlink_single_dep (dep
, pos
, a
);
956 link_range_dep (GnmDepContainer
*deps
, GnmDependent
*dep
,
957 DependencyRange
const *r
)
959 int i
= BUCKET_OF_ROW (r
->range
.start
.row
);
960 int end
= BUCKET_OF_ROW (r
->range
.end
.row
);
961 DependencyRange r2
= *r
;
964 * It is possible to see ranges bigger than the sheet when
965 * operating with 3D ranges. See bug #704109.
967 end
= MIN (end
, deps
->buckets
- 1);
969 for ( ; i
<= end
; i
++) {
970 DependencyRange
*result
;
972 /* Restrict range to bucket. */
973 r2
.range
.start
.row
= MAX (r
->range
.start
.row
, BUCKET_START_ROW (i
));
974 r2
.range
.end
.row
= MIN (r
->range
.end
.row
, BUCKET_END_ROW (i
));
976 if (deps
->range_hash
[i
] == NULL
)
977 deps
->range_hash
[i
] = g_hash_table_new (
978 (GHashFunc
) deprange_hash
,
979 (GEqualFunc
) deprange_equal
);
981 result
= g_hash_table_lookup (deps
->range_hash
[i
], &r2
);
983 /* Inserts if it is not already there */
984 micro_hash_insert (&result
->deps
, dep
);
989 /* Create a new DependencyRange structure */
990 result
= go_mem_chunk_alloc (deps
->range_pool
);
992 micro_hash_init (&result
->deps
, dep
);
993 g_hash_table_insert (deps
->range_hash
[i
], result
, result
);
998 unlink_range_dep (GnmDepContainer
*deps
, GnmDependent
*dep
,
999 DependencyRange
const *r
)
1001 int i
= BUCKET_OF_ROW (r
->range
.start
.row
);
1002 int end
= BUCKET_OF_ROW (r
->range
.end
.row
);
1003 DependencyRange r2
= *r
;
1008 end
= MIN (end
, deps
->buckets
- 1);
1010 for ( ; i
<= end
; i
++) {
1011 DependencyRange
*result
;
1013 /* Restrict range to bucket. */
1014 r2
.range
.start
.row
= MAX (r
->range
.start
.row
, BUCKET_START_ROW (i
));
1015 r2
.range
.end
.row
= MIN (r
->range
.end
.row
, BUCKET_END_ROW (i
));
1017 result
= g_hash_table_lookup (deps
->range_hash
[i
], &r2
);
1019 micro_hash_remove (&result
->deps
, dep
);
1020 if (micro_hash_is_empty (&result
->deps
)) {
1021 g_hash_table_remove (deps
->range_hash
[i
], result
);
1022 micro_hash_release (&result
->deps
);
1023 go_mem_chunk_free (deps
->range_pool
, result
);
1030 link_unlink_range_dep (GnmDepContainer
*deps
, GnmDependent
*dep
,
1031 DependencyRange
const *r
, gboolean qlink
)
1034 link_range_dep (deps
, dep
, r
);
1036 unlink_range_dep (deps
, dep
, r
);
1039 static GnmDependentFlags
1040 link_unlink_cellrange_dep (GnmDependent
*dep
, GnmCellPos
const *pos
,
1041 GnmCellRef
const *a
, GnmCellRef
const *b
,
1044 DependencyRange range
;
1045 GnmDependentFlags flag
= DEPENDENT_NO_FLAG
;
1047 gnm_cellpos_init_cellref (&range
.range
.start
, a
, pos
, dep
->sheet
);
1048 gnm_cellpos_init_cellref (&range
.range
.end
, b
, pos
, dep
->sheet
);
1049 range_normalize (&range
.range
);
1051 if (a
->sheet
!= NULL
) {
1052 if (a
->sheet
!= dep
->sheet
)
1053 flag
= (a
->sheet
->workbook
!= dep
->sheet
->workbook
)
1054 ? DEPENDENT_GOES_INTERBOOK
: DEPENDENT_GOES_INTERSHEET
;
1056 if (b
->sheet
!= NULL
&& a
->sheet
!= b
->sheet
) {
1057 Workbook
const *wb
= a
->sheet
->workbook
;
1058 int i
= a
->sheet
->index_in_wb
;
1059 int stop
= b
->sheet
->index_in_wb
;
1060 if (i
> stop
) { int tmp
= i
; i
= stop
; stop
= tmp
; }
1062 g_return_val_if_fail (b
->sheet
->workbook
== wb
, flag
);
1065 Sheet
*sheet
= g_ptr_array_index (wb
->sheets
, i
);
1067 link_unlink_range_dep (sheet
->deps
, dep
, &range
,
1070 flag
|= DEPENDENT_HAS_3D
;
1072 link_unlink_range_dep (a
->sheet
->deps
, dep
, &range
,
1075 link_unlink_range_dep (dep
->sheet
->deps
, dep
, &range
, qlink
);
1080 static GnmDependentFlags
1081 link_unlink_expr_dep (GnmEvalPos
*ep
, GnmExpr
const *tree
, gboolean qlink
)
1083 g_return_val_if_fail (tree
!= NULL
, DEPENDENT_NO_FLAG
);
1085 switch (GNM_EXPR_GET_OPER (tree
)) {
1086 case GNM_EXPR_OP_RANGE_CTOR
: /* See #562363 */
1087 case GNM_EXPR_OP_INTERSECT
:
1088 case GNM_EXPR_OP_ANY_BINARY
:
1089 return link_unlink_expr_dep (ep
, tree
->binary
.value_a
, qlink
) |
1090 link_unlink_expr_dep (ep
, tree
->binary
.value_b
, qlink
);
1091 case GNM_EXPR_OP_ANY_UNARY
:
1092 return link_unlink_expr_dep (ep
, tree
->unary
.value
, qlink
);
1093 case GNM_EXPR_OP_CELLREF
:
1094 return link_unlink_single_dep (ep
->dep
, dependent_pos (ep
->dep
), &tree
->cellref
.ref
, qlink
);
1096 case GNM_EXPR_OP_CONSTANT
:
1097 /* TODO: pass in eval flags so that we can use implicit
1100 if (VALUE_IS_CELLRANGE (tree
->constant
.value
))
1101 return link_unlink_cellrange_dep
1102 (ep
->dep
, dependent_pos (ep
->dep
),
1103 &tree
->constant
.value
->v_range
.cell
.a
,
1104 &tree
->constant
.value
->v_range
.cell
.b
,
1106 return DEPENDENT_NO_FLAG
;
1109 * FIXME: needs to be taught implicit intersection +
1110 * more cunning handling of argument type matching.
1112 case GNM_EXPR_OP_FUNCALL
: {
1114 GnmFuncEvalInfo fei
;
1115 GnmDependentFlags flag
;
1117 gnm_func_load_if_stub (tree
->func
.func
);
1119 fei
.func_call
= &tree
->func
;
1120 flag
= gnm_func_link_dep (tree
->func
.func
, &fei
, qlink
);
1122 if (!(flag
& DEPENDENT_IGNORE_ARGS
))
1123 for (i
= 0; i
< tree
->func
.argc
; i
++)
1124 flag
|= link_unlink_expr_dep (ep
, tree
->func
.argv
[i
], qlink
);
1128 case GNM_EXPR_OP_NAME
:
1130 expr_name_add_dep (tree
->name
.name
, ep
->dep
);
1132 expr_name_remove_dep (tree
->name
.name
, ep
->dep
);
1133 if (expr_name_is_active (tree
->name
.name
))
1134 return link_unlink_expr_dep (ep
, tree
->name
.name
->texpr
->expr
, qlink
) | DEPENDENT_USES_NAME
;
1135 return DEPENDENT_USES_NAME
;
1137 case GNM_EXPR_OP_ARRAY_ELEM
: {
1138 /* Non-corner cells depend on the corner */
1140 GnmCellPos
const *pos
= dependent_pos (ep
->dep
);
1141 // We cannot support array expressions unless we have
1143 g_return_val_if_fail (pos
!= NULL
, DEPENDENT_NO_FLAG
);
1145 a
.col_relative
= a
.row_relative
= FALSE
;
1146 a
.sheet
= ep
->dep
->sheet
;
1147 a
.col
= pos
->col
- tree
->array_elem
.x
;
1148 a
.row
= pos
->row
- tree
->array_elem
.y
;
1150 return link_unlink_single_dep (ep
->dep
, pos
, &a
, qlink
);
1153 case GNM_EXPR_OP_ARRAY_CORNER
: {
1154 // It's mildly unclean to do this here. We need the texpr, so get the cell.
1155 GnmCellPos
const *cpos
= dependent_pos (ep
->dep
);
1156 GnmCell
const *cell
= sheet_cell_get (ep
->dep
->sheet
, cpos
->col
, cpos
->row
);
1157 GnmEvalPos pos
= *ep
;
1158 pos
.array_texpr
= cell
->base
.texpr
;
1159 /* Corner cell depends on the contents of the expr */
1160 return link_unlink_expr_dep (&pos
, tree
->array_corner
.expr
, qlink
);
1163 case GNM_EXPR_OP_SET
: {
1165 GnmDependentFlags res
= DEPENDENT_NO_FLAG
;
1167 for (i
= 0; i
< tree
->set
.argc
; i
++)
1168 res
|= link_unlink_expr_dep (ep
, tree
->set
.argv
[i
], qlink
);
1171 #ifndef DEBUG_SWITCH_ENUM
1173 g_assert_not_reached ();
1176 return DEPENDENT_NO_FLAG
;
1180 workbook_link_3d_dep (GnmDependent
*dep
)
1182 Workbook
*wb
= dep
->sheet
->workbook
;
1184 if (wb
->being_reordered
)
1186 if (wb
->sheet_order_dependents
== NULL
)
1187 wb
->sheet_order_dependents
=
1188 g_hash_table_new (g_direct_hash
, g_direct_equal
);
1189 g_hash_table_insert (wb
->sheet_order_dependents
, dep
, dep
);
1193 workbook_unlink_3d_dep (GnmDependent
*dep
)
1195 Workbook
*wb
= dep
->sheet
->workbook
;
1197 /* during destruction */
1198 if (wb
->sheet_order_dependents
== NULL
)
1201 if (wb
->being_reordered
)
1204 g_hash_table_remove (wb
->sheet_order_dependents
, dep
);
1208 * gnm_dep_style_dependency:
1212 * @accum: (inout) (transfer full) (element-type GnmDependent):
1215 gnm_dep_style_dependency (Sheet
*sheet
,
1216 GnmExprTop
const *texpr
,
1223 * FIXME: Maybe do better for an expression that is just an
1227 for (row
= r
->start
.row
; row
<= r
->end
.row
; row
++) {
1228 for (col
= r
->start
.col
; col
<= r
->end
.col
; col
++) {
1229 GnmStyleDependent
*sd
= g_new0 (GnmStyleDependent
, 1);
1230 GnmDependent
*dep
= &sd
->base
;
1233 dep
->flags
= DEPENDENT_STYLE
;
1238 dependent_set_expr (dep
, texpr
);
1239 dependent_link (dep
);
1240 g_ptr_array_add (accum
, dep
);
1245 /*****************************************************************************/
1248 cell_dep_eval (GnmDependent
*dep
)
1250 gboolean finished
= gnm_cell_eval_content (GNM_DEP_TO_CELL (dep
));
1251 (void)finished
; /* We don't currently care */
1252 dep
->flags
&= ~GNM_CELL_HAS_NEW_EXPR
;
1256 cell_dep_set_expr (GnmDependent
*dep
, GnmExprTop
const *new_texpr
)
1259 * Explicitly do not check for array subdivision, we may be
1260 * replacing the corner of an array.
1262 gnm_cell_set_expr_unsafe (GNM_DEP_TO_CELL (dep
), new_texpr
);
1266 cell_dep_changed (GnmDependent
*dep
)
1268 /* When a cell changes, so do its dependents. */
1270 GSList
*deps
= cell_list_deps (GNM_DEP_TO_CELL (dep
));
1271 GSList
*waste
= NULL
, *work
= NULL
;
1272 GSList
*next
, *list
;
1273 for (list
= deps
; list
!= NULL
; list
= next
) {
1274 GnmDependent
*dep
= list
->data
;
1276 if (dependent_needs_recalc (dep
)) {
1280 dependent_flag_recalc (dep
);
1285 g_slist_free (waste
);
1290 cell_dep_pos (GnmDependent
const *dep
)
1292 return &GNM_DEP_TO_CELL (dep
)->pos
;
1296 cell_dep_debug_name (GnmDependent
const *dep
, GString
*target
)
1298 g_string_append (target
, cell_name (GNM_DEP_TO_CELL (dep
)));
1301 /*****************************************************************************/
1303 * "Managed" dependent handling.
1305 * A managed dependent is simply an expression set up to be maintained by
1306 * the dependency system so it follows row/column insert/delete nicely.
1308 * The expression being managed is typically a cell range, but can be any
1309 * expression. Everything is interpreted relative to A1 in the sheet.
1313 dependent_managed_init (GnmDependent
*dep
, Sheet
*sheet
)
1315 memset (dep
, 0, sizeof (*dep
));
1316 dep
->flags
= DEPENDENT_MANAGED
;
1321 dependent_managed_set_expr (GnmDependent
*dep
, GnmExprTop
const *texpr
)
1323 g_return_if_fail (dep
!= NULL
);
1325 /* We need some kind of IS_A */
1326 g_return_if_fail (dependent_type (dep
) == DEPENDENT_MANAGED
);
1329 dependent_set_expr (dep
, texpr
);
1330 if (texpr
&& dep
->sheet
)
1331 dependent_link (dep
);
1335 dependent_managed_set_sheet (GnmDependent
*dep
, Sheet
*sheet
)
1337 GnmExprTop
const *texpr
;
1339 g_return_if_fail (dep
!= NULL
);
1341 /* We need some kind of IS_A */
1342 g_return_if_fail (dependent_type (dep
) == DEPENDENT_MANAGED
);
1345 if (dep
->sheet
== sheet
)
1349 if (texpr
) gnm_expr_top_ref (texpr
);
1350 dependent_set_expr (dep
, NULL
);
1351 /* We're now unlinked from everything. */
1353 dependent_managed_set_expr (dep
, texpr
);
1354 if (texpr
) gnm_expr_top_unref (texpr
);
1358 managed_dep_debug_name (GnmDependent
const *dep
, GString
*target
)
1360 g_string_append_printf (target
, "Managed%p", (void *)dep
);
1363 /*****************************************************************************/
1366 debug_style_deps (void)
1368 static int debug
= -1;
1370 debug
= gnm_debug_flag ("style-deps");
1375 style_dep_unrender (GnmDependent
*dep
, const char *what
)
1377 GnmCellPos
const *pos
= dependent_pos (dep
);
1379 Sheet
*sheet
= dep
->sheet
;
1381 if (debug_style_deps ())
1382 g_printerr ("StyleDep %p at %s %s\n",
1383 dep
, cellpos_as_string (pos
), what
);
1386 * If the cell exists, unrender it so format changes can take
1389 cell
= sheet_cell_get (sheet
, pos
->col
, pos
->row
);
1391 gnm_cell_unrender (cell
);
1393 sheet_redraw_region (sheet
,
1395 pos
->col
, pos
->row
);
1399 style_dep_eval (GnmDependent
*dep
)
1402 * It is possible that the cell has been rendered between ::changed
1403 * was called and now.
1405 style_dep_unrender (dep
, "being evaluated");
1409 style_dep_changed (GnmDependent
*dep
)
1411 style_dep_unrender (dep
, "changed");
1416 style_dep_pos (GnmDependent
const *dep
)
1418 return &((GnmStyleDependent
*)dep
)->pos
;
1422 style_dep_debug_name (GnmDependent
const *dep
, GString
*target
)
1424 g_string_append_printf (target
, "StyleDep@%s[%p]",
1425 cellpos_as_string (style_dep_pos (dep
)),
1429 /*****************************************************************************/
1432 name_dep_debug_name (GnmDependent
const *dep
, GString
*target
)
1434 g_string_append_printf (target
, "Name%p", (void *)dep
);
1437 /*****************************************************************************/
1440 dynamic_dep_changed (GnmDependent
*dep
)
1442 DynamicDep
const *dyn
= (DynamicDep
*)dep
;
1444 /* When a dynamic dependent changes, we mark its container. */
1446 if (dependent_needs_recalc (dyn
->container
))
1449 dependent_flag_recalc (dyn
->container
);
1450 return g_slist_prepend (NULL
, dyn
->container
);
1454 dynamic_dep_debug_name (GnmDependent
const *dep
, GString
*target
)
1456 g_string_append_printf (target
, "DynamicDep%p", (void *)dep
);
1460 dependent_add_dynamic_dep (GnmDependent
*dep
, GnmRangeRef
const *rr
)
1462 GnmDependentFlags flags
;
1464 GnmCellPos
const *pos
;
1465 DependencyRange range
;
1467 g_return_if_fail (dep
!= NULL
);
1469 pos
= dependent_pos (dep
);
1471 if (dep
->flags
& DEPENDENT_HAS_DYNAMIC_DEPS
)
1472 dyn
= g_hash_table_lookup (dep
->sheet
->deps
->dynamic_deps
, dep
);
1474 dep
->flags
|= DEPENDENT_HAS_DYNAMIC_DEPS
;
1475 dyn
= g_new (DynamicDep
, 1);
1476 dyn
->base
.flags
= DEPENDENT_DYNAMIC_DEP
;
1477 dyn
->base
.sheet
= dep
->sheet
;
1478 dyn
->base
.texpr
= NULL
;
1479 dyn
->container
= dep
;
1481 dyn
->singles
= NULL
;
1482 g_hash_table_insert (dep
->sheet
->deps
->dynamic_deps
, dep
, dyn
);
1485 gnm_cellpos_init_cellref (&range
.range
.start
, &rr
->a
, pos
, dep
->sheet
);
1486 gnm_cellpos_init_cellref (&range
.range
.end
, &rr
->b
, pos
, dep
->sheet
);
1487 if (range_is_singleton (&range
.range
)) {
1488 flags
= link_single_dep (&dyn
->base
, pos
, &rr
->a
);
1489 dyn
->singles
= g_slist_prepend (dyn
->singles
, gnm_rangeref_dup (rr
));
1491 flags
= link_unlink_cellrange_dep (&dyn
->base
, pos
, &rr
->a
, &rr
->b
, TRUE
);
1492 dyn
->ranges
= g_slist_prepend (dyn
->ranges
, gnm_rangeref_dup (rr
));
1494 if (flags
& DEPENDENT_HAS_3D
)
1495 workbook_link_3d_dep (dep
);
1499 dependent_clear_dynamic_deps (GnmDependent
*dep
)
1501 g_hash_table_remove (dep
->sheet
->deps
->dynamic_deps
, dep
);
1504 /*****************************************************************************/
1508 * @dep: the dependent that changed
1510 * Adds the dependent to the workbook wide list of dependents.
1513 dependent_link (GnmDependent
*dep
)
1518 g_return_if_fail (dep
!= NULL
);
1519 g_return_if_fail (dep
->texpr
!= NULL
);
1520 g_return_if_fail (!(dep
->flags
& DEPENDENT_IS_LINKED
));
1521 g_return_if_fail (IS_SHEET (dep
->sheet
));
1522 g_return_if_fail (dep
->sheet
->deps
!= NULL
);
1526 /* Make this the new tail of the dependent list. */
1527 dep
->prev_dep
= sheet
->deps
->tail
;
1528 dep
->next_dep
= NULL
;
1530 dep
->prev_dep
->next_dep
= dep
;
1532 sheet
->deps
->head
= dep
; /* first element */
1533 sheet
->deps
->tail
= dep
;
1534 dep
->flags
|= DEPENDENT_IS_LINKED
|
1535 link_unlink_expr_dep (eval_pos_init_dep (&ep
, dep
),
1536 dep
->texpr
->expr
, TRUE
);
1538 if (dep
->flags
& DEPENDENT_HAS_3D
)
1539 workbook_link_3d_dep (dep
);
1544 * @dep: the dependent that changed
1546 * Removes the dependent from its container's set of dependents and always
1547 * removes the linkages to what it depends on.
1550 dependent_unlink (GnmDependent
*dep
)
1552 GnmDepContainer
*contain
;
1555 g_return_if_fail (dep
!= NULL
);
1556 g_return_if_fail (dependent_is_linked (dep
));
1557 g_return_if_fail (dep
->texpr
!= NULL
);
1558 g_return_if_fail (IS_SHEET (dep
->sheet
));
1560 link_unlink_expr_dep (eval_pos_init_dep (&ep
, dep
),
1561 dep
->texpr
->expr
, FALSE
);
1562 contain
= dep
->sheet
->deps
;
1563 if (contain
!= NULL
) {
1564 if (contain
->head
== dep
)
1565 contain
->head
= dep
->next_dep
;
1566 if (contain
->tail
== dep
)
1567 contain
->tail
= dep
->prev_dep
;
1569 dep
->next_dep
->prev_dep
= dep
->prev_dep
;
1571 dep
->prev_dep
->next_dep
= dep
->next_dep
;
1573 if (dep
->flags
& DEPENDENT_HAS_DYNAMIC_DEPS
)
1574 dependent_clear_dynamic_deps (dep
);
1577 if (dep
->flags
& DEPENDENT_HAS_3D
)
1578 workbook_unlink_3d_dep (dep
);
1579 dep
->flags
&= ~DEPENDENT_LINK_FLAGS
;
1583 * gnm_cell_eval_content:
1584 * @cell: the cell to evaluate.
1586 * This function evaluates the contents of the cell,
1587 * it should not be used by anyone. It is an internal
1591 gnm_cell_eval_content (GnmCell
*cell
)
1593 static GnmCell
*iterating
= NULL
;
1598 if (!gnm_cell_has_expr (cell
) || /* plain cells without expr */
1599 !dependent_is_linked (&cell
->base
)) /* special case within TABLE */
1602 #ifdef DEBUG_EVALUATION
1605 char *str
= gnm_expr_top_as_string
1607 parse_pos_init_cell (&pp
, cell
),
1609 g_printerr ("{\nEvaluating %s!%s: %s;\n",
1610 cell
->base
.sheet
->name_quoted
, cell_name (cell
),
1616 /* This is the bottom of a cycle */
1617 if (cell
->base
.flags
& DEPENDENT_BEING_CALCULATED
) {
1618 if (!cell
->base
.sheet
->workbook
->iteration
.enabled
)
1621 /* but not the first bottom */
1622 if (cell
->base
.flags
& DEPENDENT_BEING_ITERATED
) {
1623 #ifdef DEBUG_EVALUATION
1624 g_printerr ("}; /* already-iterate (%d) */\n", iterating
== NULL
);
1626 return iterating
== NULL
;
1629 /* if we are still marked as iterating then make this the last
1632 if (iterating
== cell
) {
1633 #ifdef DEBUG_EVALUATION
1634 puts ("}; /* NO-iterate (1) */");
1637 } else if (iterating
== NULL
) {
1638 cell
->base
.flags
|= DEPENDENT_BEING_ITERATED
;
1640 #ifdef DEBUG_EVALUATION
1641 puts ("}; /* START iterate = TRUE (0) */");
1645 #ifdef DEBUG_EVALUATION
1646 puts ("}; /* other-iterate (0) */");
1652 /* Prepare to calculate */
1653 eval_pos_init_cell (&pos
, cell
);
1654 cell
->base
.flags
|= DEPENDENT_BEING_CALCULATED
;
1657 * I'm somewhat afraid of thinking about the semantics of iteration
1658 * if different workbooks have different settings.
1660 max_iteration
= cell
->base
.sheet
->workbook
->iteration
.max_number
;
1663 v
= gnm_expr_top_eval (cell
->base
.texpr
, &pos
,
1664 GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
1666 v
= value_new_error (&pos
, "Internal error");
1668 #ifdef DEBUG_EVALUATION
1671 ? value_get_as_string (v
)
1672 : g_strdup ("NULL");
1673 g_printerr ("Evaluation(%d) %s := %s\n",
1674 max_iteration
, cell_name (cell
), valtxt
);
1679 /* The top of a cycle */
1680 if (cell
->base
.flags
& DEPENDENT_BEING_ITERATED
) {
1681 cell
->base
.flags
&= ~DEPENDENT_BEING_ITERATED
;
1683 /* We just completed the last iteration, don't change things */
1684 if (iterating
&& max_iteration
-- > 0) {
1685 /* If we are within bounds make this the last round */
1686 if (value_diff (cell
->value
, v
) < cell
->base
.sheet
->workbook
->iteration
.tolerance
)
1689 #ifdef DEBUG_EVALUATION
1690 puts ("/* iterate == NULL */");
1694 value_release (cell
->value
);
1697 gnm_cell_unrender (cell
);
1698 #ifdef DEBUG_EVALUATION
1699 puts ("/* LOOP */");
1703 g_return_val_if_fail (iterating
, TRUE
);
1706 gboolean had_value
= (cell
->value
!= NULL
);
1707 if (had_value
&& value_equal (v
, cell
->value
)) {
1708 /* Value didn't change. */
1711 gboolean was_string
= had_value
&& (VALUE_IS_STRING (cell
->value
) || VALUE_IS_ERROR (cell
->value
));
1712 gboolean is_string
= VALUE_IS_STRING (v
) || VALUE_IS_ERROR (v
);
1714 if ((was_string
|| is_string
))
1715 sheet_cell_queue_respan (cell
);
1718 value_release (cell
->value
);
1721 gnm_cell_unrender (cell
);
1725 if (iterating
== cell
)
1728 #ifdef DEBUG_EVALUATION
1729 g_printerr ("} (%d)\n", iterating
== NULL
);
1731 cell
->base
.flags
&= ~DEPENDENT_BEING_CALCULATED
;
1732 return iterating
== NULL
;
1740 dependent_eval (GnmDependent
*dep
)
1742 int const t
= dependent_type (dep
);
1743 GnmDependentClass
*klass
= g_ptr_array_index (dep_classes
, t
);
1745 if (dep
->flags
& DEPENDENT_HAS_DYNAMIC_DEPS
) {
1746 dependent_clear_dynamic_deps (dep
);
1747 dep
->flags
&= ~DEPENDENT_HAS_DYNAMIC_DEPS
;
1751 * Problem: this really should be a tail call.
1755 /* Don't clear flag until after in case we iterate */
1756 dep
->flags
&= ~DEPENDENT_NEEDS_RECALC
;
1760 gnm_cell_eval (GnmCell
*cell
)
1762 if (gnm_cell_needs_recalc (cell
)) {
1764 * This should be a tail call so the stack frame can be
1765 * eliminated before the call.
1767 dependent_eval (GNM_CELL_TO_DEP (cell
));
1773 * cell_queue_recalc:
1776 * Queue the cell and everything that depends on it for recalculation.
1777 * If a dependency is already queued ignore it.
1780 cell_queue_recalc (GnmCell
*cell
)
1782 g_return_if_fail (cell
!= NULL
);
1784 if (!gnm_cell_needs_recalc (cell
)) {
1787 if (gnm_cell_has_expr (cell
))
1788 dependent_flag_recalc (GNM_CELL_TO_DEP (cell
));
1790 deps
= cell_list_deps (cell
);
1791 dependent_queue_recalc_list (deps
);
1792 g_slist_free (deps
);
1800 } search_rangedeps_closure_t
;
1803 cb_search_rangedeps (gpointer key
, G_GNUC_UNUSED gpointer value
,
1806 search_rangedeps_closure_t
const *c
= closure
;
1807 DependencyRange
const *deprange
= key
;
1808 GnmRange
const *range
= &(deprange
->range
);
1811 /* When things get slow this is a good test to enable */
1812 static int counter
= 0;
1813 if ((++counter
% 100000) == 0)
1814 g_printerr ("%d\n", counter
/ 100000);
1817 if (range_contains (range
, c
->col
, c
->row
)) {
1818 GnmDepFunc func
= c
->func
;
1819 micro_hash_foreach_dep (deprange
->deps
, dep
,
1820 (func
) (dep
, c
->user
););
1825 cell_foreach_range_dep (GnmCell
const *cell
, GnmDepFunc func
, gpointer user
)
1827 search_rangedeps_closure_t closure
;
1828 GHashTable
*bucket
=
1829 cell
->base
.sheet
->deps
->range_hash
[BUCKET_OF_ROW (cell
->pos
.row
)];
1831 if (bucket
!= NULL
) {
1832 closure
.col
= cell
->pos
.col
;
1833 closure
.row
= cell
->pos
.row
;
1834 closure
.func
= func
;
1835 closure
.user
= user
;
1836 g_hash_table_foreach (bucket
, &cb_search_rangedeps
, &closure
);
1841 cell_foreach_single_dep (Sheet
const *sheet
, int col
, int row
,
1842 GnmDepFunc func
, gpointer user
)
1844 DependencySingle lookup
, *single
;
1845 GnmDepContainer
*deps
= sheet
->deps
;
1847 lookup
.pos
.col
= col
;
1848 lookup
.pos
.row
= row
;
1850 single
= g_hash_table_lookup (deps
->single_hash
, &lookup
);
1852 micro_hash_foreach_dep (single
->deps
, dep
,
1853 (*func
) (dep
, user
););
1859 * @func: (scope call):
1864 cell_foreach_dep (GnmCell
const *cell
, GnmDepFunc func
, gpointer user
)
1866 g_return_if_fail (cell
!= NULL
);
1868 /* accelerate exit */
1869 if (!cell
->base
.sheet
->deps
)
1872 cell_foreach_range_dep (cell
, func
, user
);
1873 cell_foreach_single_dep (cell
->base
.sheet
, cell
->pos
.col
, cell
->pos
.row
,
1878 cb_recalc_all_depends (gpointer key
, G_GNUC_UNUSED gpointer value
,
1879 G_GNUC_UNUSED gpointer ignore
)
1881 DependencyAny
const *depany
= key
;
1882 GSList
*work
= NULL
;
1883 micro_hash_foreach_dep (depany
->deps
, dep
, {
1884 if (!dependent_needs_recalc (dep
)) {
1885 dependent_flag_recalc (dep
);
1886 work
= g_slist_prepend (work
, dep
);
1889 dependent_queue_recalc_main (work
);
1895 cb_range_contained_depend (gpointer key
, G_GNUC_UNUSED gpointer value
,
1898 DependencyRange
const *deprange
= key
;
1899 GnmRange
const *range
= &deprange
->range
;
1900 GnmRange
const *target
= user
;
1902 if (range_overlap (target
, range
)) {
1903 GSList
*work
= NULL
;
1904 micro_hash_foreach_dep (deprange
->deps
, dep
, {
1905 if (!dependent_needs_recalc (dep
)) {
1906 dependent_flag_recalc (dep
);
1907 work
= g_slist_prepend (work
, dep
);
1910 dependent_queue_recalc_main (work
);
1915 cb_single_contained_depend (gpointer key
,
1916 G_GNUC_UNUSED gpointer value
,
1919 DependencySingle
const *depsingle
= key
;
1920 GnmRange
const *target
= user
;
1922 if (range_contains (target
, depsingle
->pos
.col
, depsingle
->pos
.row
)) {
1923 GSList
*work
= NULL
;
1924 micro_hash_foreach_dep (depsingle
->deps
, dep
, {
1925 if (!dependent_needs_recalc (dep
)) {
1926 dependent_flag_recalc (dep
);
1927 work
= g_slist_prepend (work
, dep
);
1930 dependent_queue_recalc_main (work
);
1935 * sheet_region_queue_recalc:
1936 * @sheet: The sheet.
1937 * @range: Optionally NULL range.
1939 * Queues things that depend on @sheet!@range for recalc.
1941 * If @range is NULL the entire sheet is used.
1944 sheet_region_queue_recalc (Sheet
const *sheet
, GnmRange
const *r
)
1948 g_return_if_fail (IS_SHEET (sheet
));
1949 g_return_if_fail (sheet
->deps
!= NULL
);
1952 /* mark the contained depends dirty non recursively */
1953 SHEET_FOREACH_DEPENDENT (sheet
, dep
,
1954 dependent_flag_recalc (dep
););
1956 /* look for things that depend on the sheet */
1957 for (i
= sheet
->deps
->buckets
- 1; i
>= 0 ; i
--) {
1958 GHashTable
*hash
= sheet
->deps
->range_hash
[i
];
1960 g_hash_table_foreach (hash
,
1961 &cb_recalc_all_depends
, NULL
);
1963 g_hash_table_foreach (sheet
->deps
->single_hash
,
1964 &cb_recalc_all_depends
, NULL
);
1966 int const first
= BUCKET_OF_ROW (r
->start
.row
);
1968 /* mark the contained depends dirty non recursively */
1969 SHEET_FOREACH_DEPENDENT (sheet
, dep
, {
1970 GnmCell
*cell
= GNM_DEP_TO_CELL (dep
);
1971 if (dependent_is_cell (dep
) &&
1972 range_contains (r
, cell
->pos
.col
, cell
->pos
.row
))
1973 dependent_flag_recalc (dep
);
1976 /* look for things that depend on target region */
1977 for (i
= BUCKET_OF_ROW (r
->end
.row
); i
>= first
; i
--) {
1978 GHashTable
*hash
= sheet
->deps
->range_hash
[i
];
1980 g_hash_table_foreach (hash
,
1981 &cb_range_contained_depend
, (gpointer
)r
);
1983 g_hash_table_foreach (sheet
->deps
->single_hash
,
1984 &cb_single_contained_depend
, (gpointer
)r
);
1995 GnmExprTop
const *oldtree
;
1996 } ExprRelocateStorage
;
1999 * dependents_unrelocate_free:
2002 * Free the undo info associated with a dependent relocation.
2005 dependents_unrelocate_free (GSList
*info
)
2008 for (; ptr
!= NULL
; ptr
= ptr
->next
) {
2009 ExprRelocateStorage
*tmp
= ptr
->data
;
2010 gnm_expr_top_unref (tmp
->oldtree
);
2013 g_slist_free (info
);
2017 * dependents_unrelocate:
2020 * Apply the undo info associated with a dependent relocation.
2023 dependents_unrelocate (GSList
*info
)
2026 for (; ptr
!= NULL
; ptr
= ptr
->next
) {
2027 ExprRelocateStorage
*tmp
= ptr
->data
;
2029 if (tmp
->dep_type
== DEPENDENT_CELL
) {
2030 if (!IS_SHEET (tmp
->u
.pos
.sheet
)) {
2031 /* FIXME : happens when undoing a move from a deleted
2032 * sheet. Do we want to do something here */
2034 GnmCell
*cell
= sheet_cell_get
2036 tmp
->u
.pos
.eval
.col
, tmp
->u
.pos
.eval
.row
);
2038 /* It is possible to have a NULL if the relocation info
2039 * is not really relevant. eg when undoing a pasted
2040 * cut that was relocated but also saved to a buffer */
2042 if (gnm_expr_top_is_array_corner (tmp
->oldtree
)) {
2045 gnm_expr_top_get_array_size (tmp
->oldtree
, &cols
, &rows
);
2046 gnm_cell_set_array_formula (tmp
->u
.pos
.sheet
,
2047 tmp
->u
.pos
.eval
.col
,
2048 tmp
->u
.pos
.eval
.row
,
2049 tmp
->u
.pos
.eval
.col
+ cols
- 1,
2050 tmp
->u
.pos
.eval
.row
+ rows
- 1,
2051 gnm_expr_top_new (gnm_expr_copy (gnm_expr_top_get_array_expr (tmp
->oldtree
))));
2052 cell_queue_recalc (cell
);
2053 sheet_flag_status_update_cell (cell
);
2055 sheet_cell_set_expr (cell
, tmp
->oldtree
);
2058 } else if (tmp
->dep_type
== DEPENDENT_NAME
) {
2060 dependent_set_expr (tmp
->u
.dep
, tmp
->oldtree
);
2061 dependent_flag_recalc (tmp
->u
.dep
);
2062 dependent_link (tmp
->u
.dep
);
2069 * @deps: (element-type GnmDependent): An slist of dependents.
2071 * link a list of dependents. (The list used to get freed, but is not
2075 dependents_link (GSList
*deps
)
2080 for (; ptr
!= NULL
; ptr
= ptr
->next
) {
2081 GnmDependent
*dep
= ptr
->data
;
2082 if (dep
->sheet
->being_invalidated
)
2084 if (dep
->sheet
->deps
!= NULL
&& !dependent_is_linked (dep
)) {
2085 dependent_link (dep
);
2086 dependent_queue_recalc (dep
);
2092 GnmRange
const *target
;
2097 cb_range_contained_collect (DependencyRange
const *deprange
,
2098 G_GNUC_UNUSED gpointer ignored
,
2099 CollectClosure
*user
)
2101 GnmRange
const *range
= &deprange
->range
;
2103 if (range_overlap (user
->target
, range
))
2104 micro_hash_foreach_dep (deprange
->deps
, dep
, {
2105 if (!(dep
->flags
& (DEPENDENT_FLAGGED
| DEPENDENT_CAN_RELOCATE
)) &&
2106 dependent_type (dep
) != DEPENDENT_DYNAMIC_DEP
) {
2107 dep
->flags
|= DEPENDENT_FLAGGED
;
2108 user
->list
= g_slist_prepend (user
->list
, dep
);
2113 cb_single_contained_collect (DependencySingle
const *depsingle
,
2114 G_GNUC_UNUSED gpointer ignored
,
2115 CollectClosure
*user
)
2117 if (range_contains (user
->target
, depsingle
->pos
.col
, depsingle
->pos
.row
))
2118 micro_hash_foreach_dep (depsingle
->deps
, dep
, {
2119 if (!(dep
->flags
& (DEPENDENT_FLAGGED
| DEPENDENT_CAN_RELOCATE
)) &&
2120 dependent_type (dep
) != DEPENDENT_DYNAMIC_DEP
) {
2121 dep
->flags
|= DEPENDENT_FLAGGED
;
2122 user
->list
= g_slist_prepend (user
->list
, dep
);
2127 cb_collect_names (GnmNamedExpr
*nexpr
,
2128 G_GNUC_UNUSED gpointer value
,
2131 *l
= g_slist_prepend (*l
, nexpr
);
2134 struct cb_remote_names
{
2140 cb_remote_names1 (G_GNUC_UNUSED gconstpointer key
,
2141 GnmNamedExpr
*nexpr
,
2142 struct cb_remote_names
*data
)
2144 data
->names
= g_slist_prepend (data
->names
, nexpr
);
2148 cb_remote_names2 (GnmNamedExpr
*nexpr
,
2149 G_GNUC_UNUSED gpointer value
,
2150 struct cb_remote_names
*data
)
2153 nexpr
->pos
.sheet
? nexpr
->pos
.sheet
->workbook
: nexpr
->pos
.wb
;
2155 data
->names
= g_slist_prepend (data
->names
, nexpr
);
2159 * Get a list of all names that (may) reference data in a given sheet.
2160 * This is approximated as all names in the sheet, all global names in
2161 * its workbook, and all external references that actually mention the
2165 names_referencing_sheet (Sheet
*sheet
)
2167 struct cb_remote_names data
;
2170 data
.wb
= sheet
->workbook
;
2172 workbook_foreach_name (sheet
->workbook
, TRUE
,
2173 (GHFunc
)cb_remote_names1
,
2175 gnm_sheet_foreach_name (sheet
, (GHFunc
)cb_remote_names1
, &data
);
2177 if (sheet
->deps
->referencing_names
)
2178 g_hash_table_foreach (sheet
->deps
->referencing_names
,
2179 (GHFunc
)cb_remote_names2
,
2187 * dependents_relocate:
2188 * @info: the descriptor record for what is being moved where.
2190 * Fixes references to or from a region that is going to be moved.
2191 * Returns: (transfer full): a list of the locations and expressions that were changed outside of
2195 dependents_relocate (GnmExprRelocateInfo
const *rinfo
)
2197 GnmExprRelocateInfo local_rinfo
;
2198 GSList
*l
, *dependents
= NULL
, *undo_info
= NULL
;
2202 CollectClosure collect
;
2203 GOUndo
*u_exprs
, *u_names
;
2205 g_return_val_if_fail (rinfo
!= NULL
, NULL
);
2207 /* short circuit if nothing would move */
2208 if (rinfo
->col_offset
== 0 && rinfo
->row_offset
== 0 &&
2209 rinfo
->origin_sheet
== rinfo
->target_sheet
)
2212 sheet
= rinfo
->origin_sheet
;
2215 /* collect contained cells with expressions */
2216 SHEET_FOREACH_DEPENDENT (rinfo
->origin_sheet
, dep
, {
2217 GnmCell
*cell
= GNM_DEP_TO_CELL (dep
);
2218 if (dependent_is_cell (dep
) &&
2219 range_contains (r
, cell
->pos
.col
, cell
->pos
.row
)) {
2220 dependents
= g_slist_prepend (dependents
, dep
);
2221 dep
->flags
|= DEPENDENT_FLAGGED
;
2225 /* collect the things that depend on source region */
2227 collect
.list
= dependents
;
2228 g_hash_table_foreach (sheet
->deps
->single_hash
,
2229 (GHFunc
) &cb_single_contained_collect
,
2230 (gpointer
)&collect
);
2232 int const first
= BUCKET_OF_ROW (r
->start
.row
);
2234 for (i
= BUCKET_OF_ROW (r
->end
.row
); i
>= first
; i
--) {
2235 hash
= sheet
->deps
->range_hash
[i
];
2237 g_hash_table_foreach (hash
,
2238 (GHFunc
) &cb_range_contained_collect
,
2239 (gpointer
)&collect
);
2242 dependents
= collect
.list
;
2243 local_rinfo
= *rinfo
;
2244 for (l
= dependents
; l
; l
= l
->next
) {
2245 GnmExprTop
const *newtree
;
2246 GnmDependent
*dep
= l
->data
;
2248 dep
->flags
&= ~DEPENDENT_FLAGGED
;
2249 sheet_flag_status_update_range (dep
->sheet
, NULL
);
2251 parse_pos_init_dep (&local_rinfo
.pos
, dep
);
2253 /* it is possible nothing changed for contained deps
2254 * using absolute references */
2255 newtree
= gnm_expr_top_relocate (dep
->texpr
, &local_rinfo
, FALSE
);
2256 if (newtree
!= NULL
) {
2257 int const t
= dependent_type (dep
);
2258 ExprRelocateStorage
*tmp
=
2259 g_new (ExprRelocateStorage
, 1);
2262 if (t
== DEPENDENT_NAME
) {
2263 #warning "What should we do here and why do we leak tmp?"
2265 if (t
== DEPENDENT_CELL
)
2266 tmp
->u
.pos
= local_rinfo
.pos
;
2269 tmp
->oldtree
= dep
->texpr
;
2270 gnm_expr_top_ref (tmp
->oldtree
);
2271 undo_info
= g_slist_prepend (undo_info
, tmp
);
2273 dependent_set_expr (dep
, newtree
); /* unlinks */
2274 gnm_expr_top_unref (newtree
);
2276 /* queue the things that depend on the changed dep
2277 * even if it is going to move.
2279 dependent_queue_recalc (dep
);
2281 /* relink if it is not going to move, if it is moving
2282 * then the caller is responsible for relinking.
2283 * This avoids a link/unlink/link tuple
2285 if (t
== DEPENDENT_CELL
) {
2286 GnmCellPos
const *pos
= &GNM_DEP_TO_CELL (dep
)->pos
;
2287 if (dep
->sheet
!= sheet
||
2288 !range_contains (r
, pos
->col
, pos
->row
))
2289 dependent_link (dep
);
2291 dependent_link (dep
);
2295 * The expression may not be changing, but it depends
2296 * on something that is. Not-corner array cells go here
2299 dependent_queue_recalc (dep
);
2302 /* Not the most efficient, but probably not too bad. It is
2303 * definitely cheaper than finding the set of effected sheets. */
2304 sheet_flag_status_update_range (dep
->sheet
, NULL
);
2306 g_slist_free (dependents
);
2308 u_exprs
= go_undo_unary_new (undo_info
,
2309 (GOUndoUnaryFunc
)dependents_unrelocate
,
2310 (GFreeFunc
)dependents_unrelocate_free
);
2314 switch (rinfo
->reloc_type
) {
2315 case GNM_EXPR_RELOCATE_INVALIDATE_SHEET
:
2316 case GNM_EXPR_RELOCATE_MOVE_RANGE
:
2319 case GNM_EXPR_RELOCATE_COLS
:
2320 case GNM_EXPR_RELOCATE_ROWS
: {
2321 GSList
*l
, *names
= names_referencing_sheet (sheet
);
2322 GnmExprRelocateInfo rinfo2
= *rinfo
;
2324 for (l
= names
; l
; l
= l
->next
) {
2325 GnmNamedExpr
*nexpr
= l
->data
;
2326 GnmExprTop
const *newtree
;
2327 rinfo2
.pos
= nexpr
->pos
;
2328 newtree
= gnm_expr_top_relocate (nexpr
->texpr
,
2331 GOUndo
*u
= expr_name_set_expr_undo_new (nexpr
);
2332 u_names
= go_undo_combine (u_names
, u
);
2333 expr_name_set_expr (nexpr
, newtree
);
2336 g_slist_free (names
);
2340 g_assert_not_reached ();
2344 return go_undo_combine (u_exprs
, u_names
);
2347 /*******************************************************************/
2350 cb_collect_range (G_GNUC_UNUSED gpointer key
,
2351 DependencyAny
*depany
,
2354 *collector
= g_slist_prepend (*collector
, depany
);
2359 dep_hash_destroy (GHashTable
*hash
, GSList
**dyn_deps
, Sheet
*sheet
)
2361 GSList
*deps
= NULL
, *l
;
2362 GnmExprRelocateInfo rinfo
;
2363 GSList
*deplist
= NULL
;
2364 gboolean destroy
= (sheet
->revive
== NULL
);
2366 /* We collect first because we will be changing the hash. */
2368 g_hash_table_foreach_remove (hash
,
2369 (GHRFunc
)cb_collect_range
,
2371 g_hash_table_destroy (hash
);
2373 g_hash_table_foreach (hash
, (GHFunc
)cb_collect_range
, &deps
);
2376 for (l
= deps
; l
; l
= l
->next
) {
2377 DependencyAny
*depany
= l
->data
;
2379 micro_hash_foreach_dep (depany
->deps
, dep
, {
2380 if (dependent_type (dep
) == DEPENDENT_DYNAMIC_DEP
) {
2381 GnmDependent
*c
= ((DynamicDep
*)dep
)->container
;
2382 if (!c
->sheet
->being_invalidated
)
2384 g_slist_prepend (*dyn_deps
, c
);
2385 } else if (!dep
->sheet
->being_invalidated
) {
2387 * We collect here instead of doing right away as
2388 * the dependent_set_expr below can change the
2389 * container we are looping over.
2391 deplist
= g_slist_prepend (deplist
, dep
);
2396 micro_hash_release (&depany
->deps
);
2398 g_slist_free (deps
);
2401 * We do this after the above loop as this loop will
2402 * invalidate some of the DependencyAny pointers from
2403 * above. The testcase for that is 314207, deleting
2404 * all but the first sheet in one go.
2406 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_INVALIDATE_SHEET
;
2407 for (l
= deplist
; l
; l
= l
->next
) {
2408 GnmDependent
*dep
= l
->data
;
2409 GnmExprTop
const *te
= dep
->texpr
;
2410 /* We are told this dependent depends on this region, hence if
2411 * newtree is null then either
2412 * 1) we did not depend on it (ie., serious breakage )
2413 * 2) we had a duplicate reference and we have already removed it.
2414 * 3) We depended on things via a name which will be
2415 * invalidated elsewhere */
2416 GnmExprTop
const *newtree
= gnm_expr_top_relocate (te
, &rinfo
, FALSE
);
2417 if (newtree
!= NULL
) {
2421 gnm_dep_set_expr_undo_new (dep
));
2422 dependent_set_expr (dep
, newtree
);
2423 gnm_expr_top_unref (newtree
);
2424 dependent_link (dep
);
2427 g_slist_free (deplist
);
2431 cb_collect_deps_of_name (GnmDependent
*dep
, G_GNUC_UNUSED gpointer value
,
2434 /* grab unflagged linked depends */
2435 if ((dep
->flags
& (DEPENDENT_FLAGGED
|DEPENDENT_IS_LINKED
)) == DEPENDENT_IS_LINKED
) {
2436 dep
->flags
|= DEPENDENT_FLAGGED
;
2437 *accum
= g_slist_prepend (*accum
, dep
);
2441 struct cb_collect_deps_of_names
{
2447 cb_collect_deps_of_names (GnmNamedExpr
*nexpr
,
2448 G_GNUC_UNUSED gpointer value
,
2449 struct cb_collect_deps_of_names
*accum
)
2451 accum
->names
= g_slist_prepend (accum
->names
, nexpr
);
2452 if (nexpr
->dependents
)
2453 g_hash_table_foreach (nexpr
->dependents
,
2454 (GHFunc
)cb_collect_deps_of_name
,
2459 invalidate_name (GnmNamedExpr
*nexpr
, Sheet
*sheet
)
2461 GnmExprTop
const *old_expr
= nexpr
->texpr
;
2462 GnmExprTop
const *new_expr
= NULL
;
2463 gboolean scope_being_killed
=
2465 ? nexpr
->pos
.sheet
->being_invalidated
2466 : nexpr
->pos
.wb
->during_destruction
;
2468 if (!scope_being_killed
) {
2469 GnmExprRelocateInfo rinfo
;
2470 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_INVALIDATE_SHEET
;
2471 new_expr
= gnm_expr_top_relocate (old_expr
, &rinfo
, FALSE
);
2472 g_return_if_fail (new_expr
!= NULL
);
2475 if (nexpr
->dependents
&& g_hash_table_size (nexpr
->dependents
))
2476 g_warning ("Left-over name dependencies\n");
2479 go_undo_group_add (sheet
->revive
,
2480 expr_name_set_expr_undo_new (nexpr
));
2482 expr_name_set_expr (nexpr
, new_expr
);
2486 handle_dynamic_deps (GSList
*dyn_deps
)
2490 for (ptr
= dyn_deps
; ptr
!= NULL
; ptr
= ptr
->next
) {
2491 GnmDependent
*dep
= ptr
->data
;
2492 if (dep
->flags
& DEPENDENT_HAS_DYNAMIC_DEPS
) {
2493 dependent_clear_dynamic_deps (dep
);
2494 dep
->flags
&= ~DEPENDENT_HAS_DYNAMIC_DEPS
;
2497 dependent_queue_recalc_list (dyn_deps
);
2498 g_slist_free (dyn_deps
);
2502 handle_referencing_names (GnmDepContainer
*deps
, Sheet
*sheet
)
2505 GHashTable
*names
= deps
->referencing_names
;
2506 struct cb_collect_deps_of_names accum
;
2507 gboolean destroy
= (sheet
->revive
== NULL
);
2513 deps
->referencing_names
= NULL
;
2517 g_hash_table_foreach (names
,
2518 (GHFunc
)cb_collect_deps_of_names
,
2521 for (ptr
= accum
.deps
; ptr
; ptr
= ptr
->next
) {
2522 GnmDependent
*dep
= ptr
->data
;
2523 dep
->flags
&= ~DEPENDENT_FLAGGED
;
2524 dependent_unlink (dep
);
2527 /* now that all of the dependents of these names are unlinked.
2528 * change the references in the names to avoid this sheet */
2529 for (ptr
= accum
.names
; ptr
; ptr
= ptr
->next
) {
2530 GnmNamedExpr
*nexpr
= ptr
->data
;
2531 invalidate_name (nexpr
, sheet
);
2533 g_slist_free (accum
.names
);
2535 /* then relink things en-mass in case one of the deps outside
2536 * this sheet used multiple names that referenced us */
2537 dependents_link (accum
.deps
);
2540 g_slist_free (accum
.deps
);
2541 g_hash_table_destroy (names
);
2543 go_undo_group_add (sheet
->revive
,
2544 gnm_dep_unlink_undo_new (accum
.deps
));
2549 handle_outgoing_references (GnmDepContainer
*deps
, Sheet
*sheet
)
2551 GnmDependentFlags what
= DEPENDENT_USES_NAME
;
2552 GSList
*accum
= NULL
;
2554 what
|= (sheet
->workbook
&& sheet
->workbook
->during_destruction
)
2555 ? DEPENDENT_GOES_INTERBOOK
2556 : DEPENDENT_GOES_INTERSHEET
;
2557 DEPENDENT_CONTAINER_FOREACH_DEPENDENT (deps
, dep
, {
2558 if (dependent_is_linked (dep
) && (dep
->flags
& what
)) {
2559 dependent_unlink (dep
);
2561 accum
= g_slist_prepend (accum
, dep
);
2566 go_undo_group_add (sheet
->revive
,
2567 gnm_dep_unlink_undo_new (accum
));
2572 * Invalidate references of all kinds to the sheet.
2574 * Also destroy the dependency container.
2577 do_deps_destroy (Sheet
*sheet
)
2579 GnmDepContainer
*deps
;
2580 GSList
*dyn_deps
= NULL
;
2583 g_return_if_fail (IS_SHEET (sheet
));
2584 g_return_if_fail (sheet
->being_invalidated
);
2586 /* The GnmDepContainer (i.e., sheet->deps) contains the names that
2587 * reference this, not the names it contains. Remove the locally
2588 * defined names here.
2590 * NOTE : they may continue to exist inactively for a bit. Be
2591 * careful to remove them _before_ destroying the deps. This is
2592 * a bit wasteful in that we unlink and relink a few things that
2593 * are going to be deleted. However, it is necessary to catch
2594 * all the different life cycles.
2596 gnm_named_expr_collection_free (sheet
->names
);
2597 sheet
->names
= NULL
;
2603 /* Destroy the records of what depends on this sheet. There is no need
2604 * to delicately remove individual items from the lists. The only
2605 * purpose that serves is to validate the state of our data structures.
2606 * If required this optimization can be disabled for debugging.
2609 if (sheet
->revive
) {
2610 g_object_unref (sheet
->revive
);
2611 sheet
->revive
= NULL
;
2614 for (i
= deps
->buckets
- 1; i
>= 0 ; i
--) {
2615 GHashTable
*hash
= deps
->range_hash
[i
];
2617 dep_hash_destroy (hash
, &dyn_deps
, sheet
);
2619 dep_hash_destroy (deps
->single_hash
, &dyn_deps
, sheet
);
2621 g_free (deps
->range_hash
);
2622 deps
->range_hash
= NULL
;
2624 * Note: we have not freed the elements in the pool. This call
2625 * frees everything in one go.
2627 go_mem_chunk_destroy (deps
->range_pool
, TRUE
);
2628 deps
->range_pool
= NULL
;
2630 deps
->single_hash
= NULL
;
2632 * Note: we have not freed the elements in the pool. This call
2633 * frees everything in one go.
2635 go_mem_chunk_destroy (deps
->single_pool
, TRUE
);
2636 deps
->single_pool
= NULL
;
2638 /* Now that we have tossed all deps to this sheet we can queue the
2639 * external dyn deps for recalc and free them */
2640 handle_dynamic_deps (dyn_deps
);
2642 g_hash_table_destroy (deps
->dynamic_deps
);
2643 deps
->dynamic_deps
= NULL
;
2645 handle_referencing_names (deps
, sheet
);
2647 /* Now we remove any links from dependents in this sheet to
2648 * to other containers. If the entire workbook is going away
2649 * just look for inter-book links.
2651 handle_outgoing_references (deps
, sheet
);
2657 * do_deps_invalidate:
2658 * Invalidate references of all kinds to the sheet.
2661 do_deps_invalidate (Sheet
*sheet
)
2663 GnmDepContainer
*deps
;
2664 GSList
*dyn_deps
= NULL
;
2667 g_return_if_fail (IS_SHEET (sheet
));
2668 g_return_if_fail (sheet
->being_invalidated
);
2669 g_return_if_fail (sheet
->revive
== NULL
);
2671 sheet
->revive
= go_undo_group_new ();
2673 gnm_named_expr_collection_unlink (sheet
->names
);
2677 for (i
= deps
->buckets
- 1; i
>= 0 ; i
--) {
2678 GHashTable
*hash
= deps
->range_hash
[i
];
2680 dep_hash_destroy (hash
, &dyn_deps
, sheet
);
2682 dep_hash_destroy (deps
->single_hash
, &dyn_deps
, sheet
);
2684 /* Now that we have tossed all deps to this sheet we can queue the
2685 * external dyn deps for recalc and free them */
2686 handle_dynamic_deps (dyn_deps
);
2688 handle_referencing_names (deps
, sheet
);
2690 /* Now we remove any links from dependents in this sheet to
2691 * to other containers. If the entire workbook is going away
2692 * just look for inter-book links.
2694 handle_outgoing_references (deps
, sheet
);
2698 cb_tweak_3d (GnmDependent
*dep
, G_GNUC_UNUSED gpointer value
, GSList
**deps
)
2700 *deps
= g_slist_prepend (*deps
, dep
);
2704 tweak_3d (Sheet
*sheet
)
2706 Workbook
*wb
= sheet
->workbook
;
2707 GSList
*deps
= NULL
, *l
;
2708 GnmExprRelocateInfo rinfo
;
2710 if (!wb
->sheet_order_dependents
)
2713 g_hash_table_foreach (wb
->sheet_order_dependents
,
2714 (GHFunc
)cb_tweak_3d
,
2717 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_INVALIDATE_SHEET
;
2718 for (l
= deps
; l
; l
= l
->next
) {
2719 GnmDependent
*dep
= l
->data
;
2720 GnmExprTop
const *te
= dep
->texpr
;
2721 GnmExprTop
const *newtree
= gnm_expr_top_relocate (te
, &rinfo
, FALSE
);
2723 if (newtree
!= NULL
) {
2727 gnm_dep_set_expr_undo_new (dep
));
2728 dependent_set_expr (dep
, newtree
);
2729 gnm_expr_top_unref (newtree
);
2730 dependent_link (dep
);
2731 dependent_changed (dep
);
2734 g_slist_free (deps
);
2738 dependents_invalidate_sheets (GSList
*sheets
, gboolean destroy
)
2743 /* Mark all first. */
2744 for (tmp
= sheets
; tmp
; tmp
= tmp
->next
) {
2745 Sheet
*sheet
= tmp
->data
;
2746 sheet
->being_invalidated
= TRUE
;
2750 * Fixup 3d refs that start or end on one of these sheets.
2751 * Ideally we do this one per workbook, but that is not critical
2752 * so we are not going to outright sort the sheet list.
2755 for (tmp
= sheets
; tmp
; tmp
= tmp
->next
) {
2756 Sheet
*sheet
= tmp
->data
;
2757 Workbook
*wb
= sheet
->workbook
;
2764 /* Now invalidate. */
2765 for (tmp
= sheets
; tmp
; tmp
= tmp
->next
) {
2766 Sheet
*sheet
= tmp
->data
;
2768 do_deps_destroy (sheet
);
2770 do_deps_invalidate (sheet
);
2774 for (tmp
= sheets
; tmp
; tmp
= tmp
->next
) {
2775 Sheet
*sheet
= tmp
->data
;
2776 sheet
->being_invalidated
= FALSE
;
2781 dependents_invalidate_sheet (Sheet
*sheet
, gboolean destroy
)
2785 g_return_if_fail (IS_SHEET (sheet
));
2789 dependents_invalidate_sheets (&l
, destroy
);
2793 dependents_workbook_destroy (Workbook
*wb
)
2795 g_return_if_fail (GNM_IS_WORKBOOK (wb
));
2796 g_return_if_fail (wb
->during_destruction
);
2797 g_return_if_fail (wb
->sheets
!= NULL
);
2799 /* Mark all first. */
2800 WORKBOOK_FOREACH_SHEET (wb
, sheet
, sheet
->being_invalidated
= TRUE
;);
2802 /* Kill 3d deps and workbook-level names, if needed. */
2803 if (wb
->sheet_order_dependents
) {
2804 g_hash_table_destroy (wb
->sheet_order_dependents
);
2805 wb
->sheet_order_dependents
= NULL
;
2807 gnm_named_expr_collection_free (wb
->names
);
2810 WORKBOOK_FOREACH_SHEET (wb
, sheet
, do_deps_destroy (sheet
););
2813 WORKBOOK_FOREACH_SHEET (wb
, sheet
, sheet
->being_invalidated
= FALSE
;);
2817 * dependents_revive_sheet:
2818 * Undo the effects of dependents_invalidate_sheet (sheet, FALSE).
2821 dependents_revive_sheet (Sheet
*sheet
)
2823 go_undo_undo (GO_UNDO (sheet
->revive
));
2824 g_object_unref (sheet
->revive
);
2825 sheet
->revive
= NULL
;
2827 /* Re-link local names. */
2828 gnm_named_expr_collection_relink (sheet
->names
);
2830 gnm_dep_container_sanity_check (sheet
->deps
);
2834 workbook_queue_all_recalc (Workbook
*wb
)
2836 /* FIXME: what about dependents in other workbooks? */
2837 WORKBOOK_FOREACH_DEPENDENT (wb
, dep
, dependent_flag_recalc (dep
););
2841 workbook_queue_volatile_recalc (Workbook
*wb
)
2843 WORKBOOK_FOREACH_DEPENDENT (wb
, dep
, {
2844 if (dependent_is_volatile (dep
))
2845 dependent_flag_recalc (dep
);
2854 * Computes all dependents in @wb that have been flaged as requiring
2857 * NOTE! This does not recalc dependents in other workbooks.
2860 workbook_recalc (Workbook
*wb
)
2862 gboolean redraw
= FALSE
;
2864 g_return_if_fail (GNM_IS_WORKBOOK (wb
));
2866 gnm_app_recalc_start ();
2868 WORKBOOK_FOREACH_DEPENDENT (wb
, dep
, {
2869 if (dependent_needs_recalc (dep
)) {
2871 dependent_eval (dep
);
2875 gnm_app_recalc_finish ();
2878 * This is a bit of a band-aid. If anything is recalculated, we
2879 * force a full redraw. The alternative is to ask for updates
2880 * of every cell that is changed and that is probably more
2884 WORKBOOK_FOREACH_SHEET (wb
, sheet
, {
2885 SHEET_FOREACH_VIEW (sheet
, sv
, gnm_sheet_view_flag_selection_change (sv
););
2886 sheet_redraw_all (sheet
, FALSE
);});
2891 * workbook_recalc_all:
2894 * Queues all dependents for recalc and recalculates.
2897 workbook_recalc_all (Workbook
*wb
)
2899 workbook_queue_all_recalc (wb
);
2901 /* Recalculate this workbook unconditionally. */
2902 workbook_recalc (wb
);
2904 /* Recalculate other workbooks when needed. */
2907 WORKBOOK_FOREACH_VIEW (wb
, view
,
2908 sheet_update (wb_view_cur_sheet (view
)););
2912 dynamic_dep_free (DynamicDep
*dyn
)
2914 GnmDependent
*dep
= dyn
->container
;
2915 GnmCellPos
const *pos
= dependent_pos (dep
);
2919 for (ptr
= dyn
->singles
; ptr
!= NULL
; ptr
= ptr
->next
) {
2921 unlink_single_dep (&dyn
->base
, pos
, &rr
->a
);
2924 g_slist_free (dyn
->singles
);
2925 dyn
->singles
= NULL
;
2927 for (ptr
= dyn
->ranges
; ptr
!= NULL
; ptr
= ptr
->next
) {
2929 link_unlink_cellrange_dep (&dyn
->base
, pos
,
2930 &rr
->a
, &rr
->b
, FALSE
);
2933 g_slist_free (dyn
->ranges
);
2936 if (dyn
->base
.flags
& DEPENDENT_HAS_3D
)
2937 workbook_unlink_3d_dep (&dyn
->base
);
2943 dependent_is_volatile (GnmDependent
*dep
)
2945 /* This might need to be a virtual call. */
2946 return dep
->texpr
&& gnm_expr_top_is_volatile (dep
->texpr
);
2950 * gnm_dep_container_new: (skip)
2953 * Returns: (transfer full):
2956 gnm_dep_container_new (Sheet
*sheet
)
2958 GnmDepContainer
*deps
= g_new (GnmDepContainer
, 1);
2960 deps
->head
= deps
->tail
= NULL
;
2962 deps
->buckets
= 1 + BUCKET_OF_ROW (gnm_sheet_get_last_row (sheet
));
2963 deps
->range_hash
= g_new0 (GHashTable
*, deps
->buckets
);
2964 deps
->range_pool
= go_mem_chunk_new ("range pool",
2965 sizeof (DependencyRange
),
2967 deps
->single_hash
= g_hash_table_new ((GHashFunc
) depsingle_hash
,
2968 (GEqualFunc
) depsingle_equal
);
2969 deps
->single_pool
= go_mem_chunk_new ("single pool",
2970 sizeof (DependencySingle
),
2972 deps
->referencing_names
= g_hash_table_new (g_direct_hash
,
2975 deps
->dynamic_deps
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
2976 NULL
, (GDestroyNotify
) dynamic_dep_free
);
2982 gnm_dep_container_resize (GnmDepContainer
*deps
, int rows
)
2984 int i
, buckets
= 1 + BUCKET_OF_ROW (rows
- 1);
2986 for (i
= buckets
; i
< deps
->buckets
; i
++) {
2987 GHashTable
*hash
= deps
->range_hash
[i
];
2989 int s
= g_hash_table_size (hash
);
2991 g_printerr ("Hash table size: %d\n", s
);
2992 g_hash_table_destroy (hash
);
2993 deps
->range_hash
[i
] = NULL
;
2997 deps
->range_hash
= g_renew (GHashTable
*, deps
->range_hash
, buckets
);
2999 for (i
= deps
->buckets
; i
< buckets
; i
++)
3000 deps
->range_hash
[i
] = NULL
;
3002 deps
->buckets
= buckets
;
3005 /****************************************************************************
3010 dependent_debug_name_for_sheet (GnmDependent
const *dep
, Sheet
*sheet
,
3014 GnmDependentClass
*klass
;
3016 g_return_if_fail (dep
!= NULL
);
3017 g_return_if_fail (dep_classes
);
3020 g_warning ("Invalid dep, missing sheet");
3022 if (sheet
== dep
->sheet
) {
3025 g_string_append (target
,
3026 dep
->sheet
? dep
->sheet
->name_quoted
: "?");
3027 g_string_append_c (target
, '!');
3030 t
= dependent_type (dep
);
3031 klass
= g_ptr_array_index (dep_classes
, t
);
3032 klass
->debug_name (dep
, target
);
3037 * dependent_debug_name:
3038 * @dep: The dependent we are interested in.
3040 * A useful little debugging utility.
3043 dependent_debug_name (GnmDependent
const *dep
, GString
*target
)
3045 dependent_debug_name_for_sheet (dep
, NULL
, target
);
3050 dump_range_dep (gpointer key
, G_GNUC_UNUSED gpointer value
, gpointer sheet_
)
3052 DependencyRange
const *deprange
= key
;
3053 GnmRange
const *range
= &(deprange
->range
);
3054 Sheet
*sheet
= sheet_
;
3055 GString
*target
= g_string_sized_new (10000);
3056 gboolean first
= TRUE
;
3058 g_string_append (target
, " ");
3059 g_string_append (target
, range_as_string (range
));
3060 g_string_append (target
, " -> (");
3062 micro_hash_foreach_dep (deprange
->deps
, dep
, {
3066 g_string_append (target
, ", ");
3067 dependent_debug_name_for_sheet (dep
, sheet
, target
);
3069 g_string_append_c (target
, ')');
3071 g_printerr ("%s\n", target
->str
);
3072 g_string_free (target
, TRUE
);
3076 dump_single_dep (gpointer key
, G_GNUC_UNUSED gpointer value
, gpointer sheet_
)
3078 DependencySingle
*depsingle
= key
;
3079 Sheet
*sheet
= sheet_
;
3080 GString
*target
= g_string_sized_new (10000);
3081 gboolean first
= TRUE
;
3083 g_string_append (target
, " ");
3084 g_string_append (target
, cellpos_as_string (&depsingle
->pos
));
3085 g_string_append (target
, " -> ");
3087 micro_hash_foreach_dep (depsingle
->deps
, dep
, {
3091 g_string_append (target
, ", ");
3092 dependent_debug_name_for_sheet (dep
, sheet
, target
);
3095 g_printerr ("%s\n", target
->str
);
3096 g_string_free (target
, TRUE
);
3100 dump_dynamic_dep (gpointer key
, G_GNUC_UNUSED gpointer value
,
3101 G_GNUC_UNUSED gpointer closure
)
3103 GnmDependent
*dep
= key
;
3104 DynamicDep
*dyn
= value
;
3107 GnmConventionsOut out
;
3109 out
.accum
= g_string_new (NULL
);
3111 out
.convs
= gnm_conventions_default
;
3113 pp
.wb
= dep
->sheet
->workbook
;
3114 pp
.sheet
= dep
->sheet
;
3115 pp
.eval
= *dependent_pos (dyn
->container
);
3117 g_string_append (out
.accum
, " ");
3118 dependent_debug_name (dep
, out
.accum
);
3119 g_string_append (out
.accum
, " -> ");
3120 dependent_debug_name (&dyn
->base
, out
.accum
);
3121 g_string_append (out
.accum
, " { c=");
3122 dependent_debug_name (dyn
->container
, out
.accum
);
3124 g_string_append (out
.accum
, ", s=[");
3125 for (l
= dyn
->singles
; l
; l
= l
->next
) {
3126 rangeref_as_string (&out
, l
->data
);
3128 g_string_append (out
.accum
, ", ");
3131 g_string_append (out
.accum
, "], r=[");
3132 for (l
= dyn
->ranges
; l
; l
= l
->next
) {
3133 rangeref_as_string (&out
, l
->data
);
3135 g_string_append (out
.accum
, ", ");
3138 g_string_append (out
.accum
, "] }");
3139 g_printerr ("%s\n", out
.accum
->str
);
3140 g_string_free (out
.accum
, TRUE
);
3144 * gnm_dep_container_dump:
3147 * A useful utility for checking the state of the dependency data structures.
3150 gnm_dep_container_dump (GnmDepContainer
const *deps
,
3155 g_return_if_fail (deps
!= NULL
);
3157 gnm_dep_container_sanity_check (deps
);
3159 for (i
= deps
->buckets
- 1; i
>= 0 ; i
--) {
3160 GHashTable
*hash
= deps
->range_hash
[i
];
3161 if (hash
!= NULL
&& g_hash_table_size (hash
) > 0) {
3162 g_printerr (" Bucket %d (rows %d-%d): Range hash size %d: range over which cells in list depend\n",
3164 BUCKET_START_ROW (i
) + 1,
3165 BUCKET_END_ROW (i
) + 1,
3166 g_hash_table_size (hash
));
3167 g_hash_table_foreach (hash
,
3173 if (deps
->single_hash
&& g_hash_table_size (deps
->single_hash
) > 0) {
3174 g_printerr (" Single hash size %d: cell on which list of cells depend\n",
3175 g_hash_table_size (deps
->single_hash
));
3176 g_hash_table_foreach (deps
->single_hash
,
3181 if (deps
->dynamic_deps
&& g_hash_table_size (deps
->dynamic_deps
) > 0) {
3182 g_printerr (" Dynamic hash size %d: cells that depend on dynamic dependencies\n",
3183 g_hash_table_size (deps
->dynamic_deps
));
3184 g_hash_table_foreach (deps
->dynamic_deps
,
3185 dump_dynamic_dep
, NULL
);
3188 if (deps
->referencing_names
&& g_hash_table_size (deps
->referencing_names
) > 0) {
3189 GSList
*l
, *names
= NULL
;
3191 g_hash_table_foreach (deps
->referencing_names
,
3192 (GHFunc
)cb_collect_names
,
3195 g_printerr (" Names whose expressions explicitly reference this sheet\n ");
3196 for (l
= names
; l
; l
= l
->next
) {
3197 GnmNamedExpr
*nexpr
= l
->data
;
3199 expr_name_name (nexpr
),
3200 l
->next
? ", " : "\n");
3202 g_slist_free (names
);
3207 dependents_dump (Workbook
*wb
)
3209 WORKBOOK_FOREACH_SHEET
3213 SHEET_FOREACH_DEPENDENT (sheet
, dep
, count
++;);
3214 g_printerr ("Dependencies for %s (count=%d):\n",
3215 sheet
->name_unquoted
, count
);
3216 gnm_dep_container_dump (sheet
->deps
, sheet
);
3221 gnm_dep_container_sanity_check (GnmDepContainer
const *deps
)
3223 GnmDependent
const *dep
;
3226 if (deps
->head
&& !deps
->tail
)
3227 g_warning ("Dependency container %p has head, but no tail.", (void *)deps
);
3228 if (deps
->tail
&& !deps
->head
)
3229 g_warning ("Dependency container %p has tail, but no head.", (void *)deps
);
3230 if (deps
->head
&& deps
->head
->prev_dep
)
3231 g_warning ("Dependency container %p has head, but not at the beginning.", (void *)deps
);
3232 if (deps
->tail
&& deps
->tail
->next_dep
)
3233 g_warning ("Dependency container %p has tail, but not at the end.", (void *)deps
);
3235 seenb4
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
3236 for (dep
= deps
->head
; dep
; dep
= dep
->next_dep
) {
3237 if (dep
->prev_dep
&& (dep
->prev_dep
->next_dep
!= dep
))
3238 g_warning ("Dependency container %p has left double-link failure at %p.", (void *)deps
, (void *)dep
);
3239 if (dep
->next_dep
&& (dep
->next_dep
->prev_dep
!= dep
))
3240 g_warning ("Dependency container %p has right double-link failure at %p.", (void *)deps
, (void *)dep
);
3241 if (!dep
->next_dep
&& dep
!= deps
->tail
)
3242 g_warning ("Dependency container %p ends before its tail.", (void *)deps
);
3243 if (!dependent_is_linked (dep
))
3244 g_warning ("Dependency container %p contains unlinked dependency %p.", (void *)deps
, (void *)dep
);
3245 if (g_hash_table_lookup (seenb4
, dep
)) {
3246 g_warning ("Dependency container %p is circular.", (void *)deps
);
3249 g_hash_table_insert (seenb4
, (gpointer
)dep
, (gpointer
)dep
);
3251 g_hash_table_destroy (seenb4
);
3254 /* ------------------------------------------------------------------------- */