[gnumeric] Conditional styling: rework the higher levels.
- From: Morten Welinder <mortenw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnumeric] Conditional styling: rework the higher levels.
- Date: Fri, 14 Aug 2020 01:50:11 +0000 (UTC)
commit fff62cb9489ca6d01dabf6a2537ad994b90043a7
Author: Morten Welinder <terra gnome org>
Date: Thu Aug 13 21:49:30 2020 -0400
Conditional styling: rework the higher levels.
NEWS | 3 +
src/Makefile.am | 2 +
src/dependent.c | 159 +----------
src/dependent.h | 11 +-
src/gnumeric-fwd.h | 1 +
src/mstyle.c | 113 +++-----
src/ranges.c | 19 +-
src/sheet-conditions.c | 697 +++++++++++++++++++++++++++++++++++++++++++++++++
src/sheet-conditions.h | 23 ++
src/sheet.c | 4 +
src/sheet.h | 2 +
src/style-conditions.c | 171 +++++++++++-
src/style-conditions.h | 19 +-
src/wbc-gtk.c | 8 +
14 files changed, 976 insertions(+), 256 deletions(-)
---
diff --git a/NEWS b/NEWS
index f02395f94..34a37720d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,8 @@
Gnumeric 1.12.49
+Morten:
+ * Rework conditional styling's upper level.
+
--------------------------------------------------------------------------
Gnumeric 1.12.48
diff --git a/src/Makefile.am b/src/Makefile.am
index e9267ff00..32934ded7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -447,6 +447,7 @@ libspreadsheet_la_SOURCES = \
sf-gamma.c \
sf-trig.c \
sheet-autofill.c \
+ sheet-conditions.c \
sheet-control-gui.c \
sheet-control.c \
sheet-diff.c \
@@ -586,6 +587,7 @@ libspreadsheet_include_HEADERS = \
sf-gamma.h \
sf-trig.h \
sheet-autofill.h \
+ sheet-conditions.h \
sheet-control-gui-priv.h \
sheet-control-gui.h \
sheet-control-priv.h \
diff --git a/src/dependent.c b/src/dependent.c
index fa9f768f1..95e68ab99 100644
--- a/src/dependent.c
+++ b/src/dependent.c
@@ -339,32 +339,6 @@ static const GnmDependentClass managed_dep_class = {
managed_dep_debug_name,
};
-static GnmCellPos *managed_dep_pos (GnmDependent const *dep);
-static const GnmDependentClass managed_pos_dep_class = {
- dummy_dep_eval,
- NULL,
- NULL,
- managed_dep_pos,
- managed_dep_debug_name,
-};
-
-static void style_dep_eval (GnmDependent *dep);
-static GSList *style_dep_changed (GnmDependent *dep);
-static GnmCellPos *style_dep_pos (GnmDependent const *dep);
-static void style_dep_debug_name (GnmDependent const *dep, GString *target);
-static const GnmDependentClass style_dep_class = {
- style_dep_eval,
- NULL,
- style_dep_changed,
- style_dep_pos,
- style_dep_debug_name,
-};
-typedef struct {
- GnmDependent base;
- GnmCellPos pos;
-} GnmStyleDependent;
-
-
static GPtrArray *dep_classes = NULL;
void
@@ -379,8 +353,6 @@ dependent_types_init (void)
g_ptr_array_add (dep_classes, (gpointer)&dynamic_dep_class);
g_ptr_array_add (dep_classes, (gpointer)&name_dep_class);
g_ptr_array_add (dep_classes, (gpointer)&managed_dep_class);
- g_ptr_array_add (dep_classes, (gpointer)&managed_pos_dep_class);
- g_ptr_array_add (dep_classes, (gpointer)&style_dep_class);
#if USE_POOLS
micro_few_pool =
@@ -1371,44 +1343,6 @@ workbook_unlink_3d_dep (GnmDependent *dep)
g_hash_table_remove (wb->sheet_order_dependents, dep);
}
-/**
- * gnm_dep_style_dependency:
- * @sheet:
- * @texpr:
- * @r:
- * @accum: (inout) (transfer full) (element-type GnmDependent):
- **/
-void
-gnm_dep_style_dependency (Sheet *sheet,
- GnmExprTop const *texpr,
- GnmRange const *r,
- GPtrArray *accum)
-{
- int row, col;
-
- /*
- * FIXME: Maybe do better for an expression that is just an
- * absolute ref.
- */
-
- for (row = r->start.row; row <= r->end.row; row++) {
- for (col = r->start.col; col <= r->end.col; col++) {
- GnmStyleDependent *sd = g_new0 (GnmStyleDependent, 1);
- GnmDependent *dep = &sd->base;
-
- dep->sheet = sheet;
- dep->flags = DEPENDENT_STYLE;
- dep->texpr = NULL;
- sd->pos.col = col;
- sd->pos.row = row;
-
- dependent_set_expr (dep, texpr);
- dependent_link (dep);
- g_ptr_array_add (accum, dep);
- }
- }
-}
-
/*****************************************************************************/
static void
@@ -1486,14 +1420,6 @@ dependent_managed_init (GnmDepManaged *dep, Sheet *sheet)
dep->base.sheet = sheet;
}
-void
-dependent_managed_pos_init (GnmDepManaged *dep, Sheet *sheet, GnmCellPos const *pos)
-{
- dependent_managed_init (dep, sheet);
- dep->base.flags = DEPENDENT_MANAGED_POS;
- dep->pos = *pos;
-}
-
GnmExprTop const *
dependent_managed_get_expr (GnmDepManaged const *dep)
{
@@ -1544,82 +1470,6 @@ managed_dep_debug_name (GnmDependent const *dep, GString *target)
g_string_append_printf (target, "Managed%p", (void *)dep);
}
-static GnmCellPos *
-managed_dep_pos (GnmDependent const *dep)
-{
- return &((GnmDepManaged*)dep)->pos;
-}
-
-/*****************************************************************************/
-
-static gboolean
-debug_style_deps (void)
-{
- static int debug = -1;
- if (debug < 0)
- debug = gnm_debug_flag ("style-deps");
- return debug;
-}
-
-static void
-style_dep_unrender (GnmDependent *dep, const char *what)
-{
- GnmCellPos const *pos = dependent_pos (dep);
- GnmCell *cell;
- Sheet *sheet = dep->sheet;
- GnmRange r;
-
- if (debug_style_deps ())
- g_printerr ("StyleDep %p at %s %s\n",
- dep, cellpos_as_string (pos), what);
-
- /*
- * If the cell exists, unrender it so format changes can take
- * effect.
- */
- cell = sheet_cell_get (sheet, pos->col, pos->row);
- if (cell)
- gnm_cell_unrender (cell);
-
- // Redraws may involve computation (via conditional styling,
- // for example) so doing it now is no good. See #480 for a
- // particular nasty example involving conditional styling and
- // dynamic dependents.
- range_init_cellpos (&r, pos);
- sheet_queue_redraw_range (sheet, &r);
-}
-
-static void
-style_dep_eval (GnmDependent *dep)
-{
- /*
- * It is possible that the cell has been rendered between ::changed
- * was called and now.
- */
- style_dep_unrender (dep, "being evaluated");
-}
-
-static GSList *
-style_dep_changed (GnmDependent *dep)
-{
- style_dep_unrender (dep, "changed");
- return NULL;
-}
-
-static GnmCellPos *
-style_dep_pos (GnmDependent const *dep)
-{
- return &((GnmStyleDependent*)dep)->pos;
-}
-
-static void
-style_dep_debug_name (GnmDependent const *dep, GString *target)
-{
- g_string_append_printf (target, "StyleDep@%s[%p]",
- cellpos_as_string (style_dep_pos (dep)),
- dep);
-}
-
/*****************************************************************************/
static void
@@ -3311,7 +3161,10 @@ gnm_dep_container_dump (GnmDepContainer const *deps,
gnm_dep_container_sanity_check (deps);
alldeps = g_hash_table_new (g_direct_hash, g_direct_equal);
- SHEET_FOREACH_DEPENDENT (sheet, dep, g_hash_table_insert (alldeps, dep, dep););
+ SHEET_FOREACH_DEPENDENT (sheet, dep,
+ if (!dependent_is_cell (dep))
+ g_hash_table_insert (alldeps, dep, dep);
+ );
for (i = 0; i < deps->buckets; i++) {
GHashTable *hash = deps->range_hash[i];
@@ -3350,7 +3203,7 @@ gnm_dep_container_dump (GnmDepContainer const *deps,
GHashTableIter hiter;
gpointer key, value;
- g_printerr (" Dynamic hash size %d: cells that depend on dynamic dependencies\n",
+ g_printerr (" Dynamic hash size %d: dependents that depend on dynamic dependencies\n",
g_hash_table_size (deps->dynamic_deps));
g_hash_table_iter_init (&hiter, deps->dynamic_deps);
while (g_hash_table_iter_next (&hiter, &key, &value)) {
@@ -3377,7 +3230,7 @@ gnm_dep_container_dump (GnmDepContainer const *deps,
GHashTableIter hiter;
gpointer key;
- g_printerr (" Dependencies of sheet not listed above:\n");
+ g_printerr (" Dependencies of sheet not listed above (excluding cells):\n");
g_hash_table_iter_init (&hiter, alldeps);
while (g_hash_table_iter_next (&hiter, &key, NULL)) {
GnmDependent *dep = key;
diff --git a/src/dependent.h b/src/dependent.h
index dd56ec003..943dcfecf 100644
--- a/src/dependent.h
+++ b/src/dependent.h
@@ -31,8 +31,6 @@ typedef enum {
DEPENDENT_DYNAMIC_DEP = 0x00000002, /* builtin type */
DEPENDENT_NAME = 0x00000003, /* builtin pseudo type */
DEPENDENT_MANAGED = 0x00000004, /* builtin type */
- DEPENDENT_MANAGED_POS = 0x00000005, /* builtin type */
- DEPENDENT_STYLE = 0x00000006, /* builtin type */
DEPENDENT_TYPE_MASK = 0x00000fff,
/* Linked into the workbook wide expression list */
@@ -116,11 +114,6 @@ void dependents_revive_sheet (Sheet *sheet);
void workbook_queue_all_recalc (Workbook *wb);
void workbook_queue_volatile_recalc (Workbook *wb);
-void gnm_dep_style_dependency (Sheet *sheet,
- GnmExprTop const *texpr,
- GnmRange const *r,
- GPtrArray *accum);
-
GnmDepContainer *gnm_dep_container_new (Sheet *sheet);
void gnm_dep_container_dump (GnmDepContainer const *deps,
Sheet *sheet);
@@ -132,16 +125,14 @@ void gnm_dep_container_resize (GnmDepContainer *deps, int rows);
typedef struct {
GnmDependent base;
- GnmCellPos pos;
} GnmDepManaged;
void dependent_managed_init (GnmDepManaged *dep, Sheet *sheet);
-void dependent_managed_pos_init (GnmDepManaged *dep, Sheet *sheet, GnmCellPos const *pos);
void dependent_managed_set_expr (GnmDepManaged *dep, GnmExprTop const *texpr);
GnmExprTop const *dependent_managed_get_expr (GnmDepManaged const *dep);
void dependent_managed_set_sheet (GnmDepManaged *dep, Sheet *sheet);
-
+// ----------------------------------------------------------------------------
#define DEPENDENT_CONTAINER_FOREACH_DEPENDENT(dc, dep, code) \
do { \
diff --git a/src/gnumeric-fwd.h b/src/gnumeric-fwd.h
index 92a99e88d..c2a04246d 100644
--- a/src/gnumeric-fwd.h
+++ b/src/gnumeric-fwd.h
@@ -71,6 +71,7 @@ typedef struct _GnmSearchReplace GnmSearchReplace;
typedef struct _GnmSheetSize GnmSheetSize;
typedef struct _GnmSheetSlicer GnmSheetSlicer;
typedef struct _GnmSheetStyleData GnmSheetStyleData;
+typedef struct GnmSheetConditionsData_ GnmSheetConditionsData;
typedef struct _GnmSortData GnmSortData;
typedef struct _GnmStfExport GnmStfExport;
typedef struct _GnmStyle GnmStyle;
diff --git a/src/mstyle.c b/src/mstyle.c
index 43491c59c..f0cd89c5c 100644
--- a/src/mstyle.c
+++ b/src/mstyle.c
@@ -16,6 +16,7 @@
#include <style-font.h>
#include <style-color.h>
#include <style-conditions.h>
+#include <sheet-conditions.h>
#include <validation.h>
#include <pattern.h>
#include <hlink.h>
@@ -1041,6 +1042,14 @@ gnm_style_link_sheet (GnmStyle *style, Sheet *sheet)
style = link_border_colors (style, auto_color, style_is_orig);
style_color_unref (auto_color);
+ if (elem_is_set (style, MSTYLE_CONDITIONS) && style->conditions) {
+ // We actually change the style here, but the resulting
+ // ->conditions should be equivalent.
+ GnmStyleConditions *sc_new = sheet_conditions_share_conditions_add (style->conditions);
+ if (sc_new)
+ gnm_style_set_conditions (style, g_object_ref (sc_new));
+ }
+
style->linked_sheet = sheet;
style->link_count = 1;
@@ -1066,6 +1075,8 @@ gnm_style_unlink (GnmStyle *style)
d(("unlink %p = %d\n", style, style->link_count-1));
if (style->link_count-- == 1) {
+ if (elem_is_set (style, MSTYLE_CONDITIONS) && style->conditions)
+ sheet_conditions_share_conditions_remove (style->conditions);
sheet_style_unlink (style->linked_sheet, style);
style->linked_sheet = NULL;
gnm_style_unref (style);
@@ -2304,27 +2315,6 @@ gnm_style_get_cond_style (GnmStyle const *style, int ix)
return g_ptr_array_index (style->cond_styles, ix);
}
-
-/*
- * Just a simple version for now. We can also ignore most function
- * calls[1] and self-references[2].
- *
- * [1] Excluding volatile (TODAY, ...) and those that can create references
- * outside the arguments (INDIRECT).
- *
- * [2] References that print like A1 when used in A1.
- */
-static gboolean
-cond_expr_harmless (GnmExpr const *expr)
-{
- GnmValue const *v = gnm_expr_get_constant (expr);
- if (v && !VALUE_IS_CELLRANGE (v))
- return TRUE;
-
- return FALSE;
-}
-
-
void
gnm_style_link_dependents (GnmStyle *style, GnmRange const *r)
{
@@ -2336,79 +2326,46 @@ gnm_style_link_dependents (GnmStyle *style, GnmRange const *r)
sheet = style->linked_sheet;
- /*
- * Conditional formatting.
- *
- * We need to trigger a reformatting of the cell if a cell referenced
- * by the condition changes.
- */
+ // ----------------------------------------
+
+ // Conditional formatting.
+ //
+ // We need to trigger a reformatting of the cell if a cell referenced
+ // by the condition changes.
sc = elem_is_set (style, MSTYLE_CONDITIONS)
? gnm_style_get_conditions (style)
: NULL;
if (sc) {
- GnmParsePos pp;
- GPtrArray const *conds = gnm_style_conditions_details (sc);
- guint ui;
-
- parse_pos_init (&pp, NULL, sheet, r->start.col, r->start.row);
-
- for (ui = 0; conds && ui < conds->len; ui++) {
- GnmStyleCond const *c = g_ptr_array_index (conds, ui);
- guint ei;
-
- for (ei = 0; ei < 2; ei++) {
- GnmExprTop const *texpr =
- gnm_style_cond_get_expr (c, ei);
- char *s = NULL;
- if (!texpr)
- continue;
-
- if (debug_style_deps)
- s = gnm_expr_top_as_string (texpr, &pp,
- sheet_get_conventions (sheet));
-
- if (cond_expr_harmless (texpr->expr)) {
- if (debug_style_deps) {
- g_printerr ("Not linking %s %d:%d for %p: %s (harmless)\n",
- range_as_string (r), ui, ei, style, s);
- g_free (s);
- }
- continue;
- }
-
- if (debug_style_deps) {
- g_printerr ("Linking %s %d:%d for %p: %s\n",
- range_as_string (r), ui, ei, style, s);
- g_free (s);
- }
-
- if (!style->deps)
- style->deps = g_ptr_array_new ();
- gnm_dep_style_dependency
- (sheet, texpr, r, style->deps);
- }
- }
+ sheet_conditions_add (sheet, r, style);
}
- /*
- * Validations.
- *
- * We can probably ignore those. If a dependent cell changes such
- * that a validation condition is no longer satisfied, it is
- * grandfathered in as valid.
- */
-
- /* The style owns the deps. */
+ // ----------------------------------------
+ // Validations.
+ //
+ // We can probably ignore those. If a dependent cell changes such
+ // that a validation condition is no longer satisfied, it is
+ // grandfathered in as valid.
}
void
gnm_style_unlink_dependents (GnmStyle *style, GnmRange const *r)
{
unsigned ui, k;
+ GnmStyleConditions *sc;
+ Sheet *sheet;
g_return_if_fail (style != NULL);
g_return_if_fail (r != NULL);
+ sheet = style->linked_sheet;
+
+ sc = elem_is_set (style, MSTYLE_CONDITIONS)
+ ? gnm_style_get_conditions (style)
+ : NULL;
+ if (sc) {
+ sheet_conditions_remove (sheet, r, style);
+ }
+
if (!style->deps)
return;
diff --git a/src/ranges.c b/src/ranges.c
index a4e786ccd..21a550c37 100644
--- a/src/ranges.c
+++ b/src/ranges.c
@@ -902,7 +902,8 @@ gnm_range_equal (const GnmRange *a, const GnmRange *b)
* Returns: a value that is negative if range @a comes before range @b;
* zero if the two ranges are equal; positive if range @a comes after
* range @b. The order imposed is lexicographical by starting row,
- * then column, then ending row, then column.
+ * then column, then ending row, then column. In other words, the order
+ * is A1, B1, A2, B2.
*/
int
gnm_range_compare (GnmRange const *a, GnmRange const *b)
@@ -915,6 +916,18 @@ gnm_range_compare (GnmRange const *a, GnmRange const *b)
return i;
}
+// Alternative with order A1, A2, B1, B2
+static int
+gnm_range_compare_alt (GnmRange const *a, GnmRange const *b)
+{
+ int i = 0;
+ if (!i) i = a->start.col - b->start.col;
+ if (!i) i = a->start.row - b->start.row;
+ if (!i) i = a->end.col - b->end.col;
+ if (!i) i = a->end.row - b->end.row;
+ return i;
+}
+
GnmSheetRange *
gnm_sheet_range_new (Sheet *sheet, GnmRange const *r)
@@ -1341,4 +1354,8 @@ gnm_range_simplify (GArray *arr)
try_merge_pair (arr, ui - 1, ui);
for (ui = arr->len - 1; ui > 0; ui--)
try_merge_pair (arr, ui - 1, ui);
+
+ g_array_sort (arr, (GCompareFunc) gnm_range_compare_alt);
+ for (ui = arr->len - 1; ui > 0; ui--)
+ try_merge_pair (arr, ui - 1, ui);
}
diff --git a/src/sheet-conditions.c b/src/sheet-conditions.c
new file mode 100644
index 000000000..5ee98a676
--- /dev/null
+++ b/src/sheet-conditions.c
@@ -0,0 +1,697 @@
+/*
+ * sheet-conditions.c: storage mechanism for conditional styles
+ *
+ * Copyright (C) 2020 Morten Welinder (terra gnome org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+// The style condition system's dependency setup
+//
+//
+// GnmStyleCondDep #1 ------------+
+// |
+// GnmStyleCondDep #2 ------------+
+// +-------- CSGroupDep
+// GnmStyleCondDep #3 ------------+
+// |
+// GnmStyleCondDep #4 ------------+
+//
+// Each GnmStyleCondDep handles one GnmStyleCond with 0-2 condition expressions
+// in it. It has a position (see below) and is hooked into the dependency for
+// just that single location. The action of a GnmStyleCondDep being changed is
+// simply to mark its CSGroupDep being changed. (This is not the main dependency
+// tracking mechanism. However, when dynamic dependents are being created using
+// condition evaluations, they will be hooked here.)
+//
+// The CSGroupDep is handling all ranges for which a certain GnmStyleConditions
+// is in use (within a single sheet). Its position and the position of all the
+// GnmStyleCondDep below it is the top left corner of the first range. It
+// carries a single artificial expression consisting of ranges from all the
+// expressions in the GnmStyleCondDep extended to account for all ranges.
+// Example: style is for range C1:D9, so the position is C1. Let the single
+// expression be A1>0 (relative to C1). The artificial expression will be
+// A1:B9 and is the range that some cell in C1:C9 depends on.
+//
+// The dependency system will see A1 (irrelevant, for the GnmStyleCondDep) and
+// A1:B9 (for the CSGroupDep) which is what really triggers changes.
+
+
+#include <gnumeric-config.h>
+#include <sheet-conditions.h>
+#include <style-conditions.h>
+#include <sheet.h>
+#include <workbook-priv.h>
+#include <ranges.h>
+#include <expr.h>
+#include <expr-impl.h>
+#include <expr-name.h>
+#include <value.h>
+#include <func.h>
+#include <mstyle.h>
+#include <gutils.h>
+
+// Do house keeping at the end of loading instead of repeatedly during.
+// (Meant to be on; setting for debugging only.)
+#define FAST_LOAD 1
+
+// Short-circuit most work at exit time.
+// (Meant to be on; setting for debugging only.)
+#define FAST_EXIT 1
+
+static gboolean debug_sheet_conds;
+
+// ----------------------------------------------------------------------------
+
+static guint csgd_get_dep_type (void);
+
+typedef struct {
+ GnmDependent base;
+ GnmCellPos pos;
+} CSGroupDep;
+
+// A group is a collection of ranges that share conditional styling. They
+// need not share GnmStyle.
+typedef struct {
+ // Dependent that gets triggered by the dependents managing the
+ // expressions inside the conditions. (Must be first.)
+ CSGroupDep dep;
+
+ // The conditional styling
+ GnmStyleConditions *conds;
+
+ // The ranges
+ GArray *ranges; // element-type: GnmRange
+} CSGroup;
+
+struct GnmSheetConditionsData_ {
+ GHashTable *groups;
+ gboolean needs_simplify;
+
+ GHashTable *linked_conditions;
+
+ gulong sig_being_loaded;
+};
+
+static void update_group (CSGroup *g);
+
+static void
+cb_free_group (CSGroup *g)
+{
+ g_array_set_size (g->ranges, 0);
+ update_group (g);
+
+ g_array_free (g->ranges, TRUE);
+ g_free (g);
+}
+
+static void
+simplify_group (CSGroup *g)
+{
+ gnm_range_simplify (g->ranges);
+ update_group (g);
+}
+
+static gboolean
+sc_equal (GnmStyleConditions const *sca, GnmStyleConditions const *scb)
+{
+ return gnm_style_conditions_equal (sca, scb, FALSE);
+}
+
+static void
+cb_being_loaded (Sheet *sheet)
+{
+ if (!sheet->workbook->being_loaded)
+ sheet_conditions_simplify (sheet);
+}
+
+void
+sheet_conditions_init (Sheet *sheet)
+{
+ GnmSheetConditionsData *cd;
+
+ debug_sheet_conds = gnm_debug_flag ("sheet-conditions");
+
+ cd = sheet->conditions = g_new0 (GnmSheetConditionsData, 1);
+ cd->groups = g_hash_table_new_full
+ (g_direct_hash, g_direct_equal,
+ NULL,
+ (GDestroyNotify)cb_free_group);
+
+ cd->linked_conditions = g_hash_table_new
+ ((GHashFunc)gnm_style_conditions_hash,
+ (GCompareFunc)sc_equal);
+
+ cd->sig_being_loaded = sheet->workbook
+ ? g_signal_connect_swapped (G_OBJECT (sheet->workbook),
+ "notify::being-loaded",
+ G_CALLBACK (cb_being_loaded),
+ sheet)
+ : 0; // a preview grid sheet
+}
+
+
+void
+sheet_conditions_uninit (Sheet *sheet)
+{
+ GnmSheetConditionsData *cd = sheet->conditions;
+
+ if (cd->sig_being_loaded) {
+ g_signal_handler_disconnect (sheet->workbook, cd->sig_being_loaded);
+ cd->sig_being_loaded = 0;
+ }
+
+ if (g_hash_table_size (cd->groups) > 0)
+ g_warning ("Left-over conditional styling.");
+
+ g_hash_table_destroy (cd->groups);
+ cd->groups = NULL;
+
+ g_hash_table_destroy (cd->linked_conditions);
+ cd->linked_conditions = NULL;
+
+ g_free (cd);
+ sheet->conditions = NULL;
+}
+
+// ----------------------------------------------------------------------------
+
+/**
+ * sheet_conditions_share_conditions_add:
+ * @conds: (transfer none):
+ *
+ * Returns: (transfer none) (nullable): Conditions equivalent to @conds, or
+ * %NULL if @conds had not been seen before.
+ */
+GnmStyleConditions *
+sheet_conditions_share_conditions_add (GnmStyleConditions *conds)
+{
+ Sheet *sheet = gnm_style_conditions_get_sheet (conds);
+ GnmSheetConditionsData *cd = sheet->conditions;
+ int n = 0;
+ GnmStyleConditions *res = NULL;
+ gpointer key, value;
+
+ if (g_hash_table_lookup_extended (cd->linked_conditions, conds, &key, &value)) {
+ res = conds = key;
+ n = GPOINTER_TO_INT (value);
+ }
+
+ g_hash_table_insert (cd->linked_conditions,
+ conds,
+ GINT_TO_POINTER (n + 1));
+ return res;
+}
+
+/**
+ * sheet_conditions_share_conditions_remove:
+ * @conds: (transfer none):
+ *
+ * This notifies the sheet conditions manager that one use of the shared
+ * conditions has gone away.
+ */
+void
+sheet_conditions_share_conditions_remove (GnmStyleConditions *conds)
+{
+ Sheet *sheet = gnm_style_conditions_get_sheet (conds);
+ GnmSheetConditionsData *cd = sheet->conditions;
+ int n = GPOINTER_TO_INT (g_hash_table_lookup (cd->linked_conditions, conds));
+
+ if (n > 1)
+ g_hash_table_insert (cd->linked_conditions,
+ conds,
+ GINT_TO_POINTER (n - 1));
+ else if (n == 1)
+ g_hash_table_remove (cd->linked_conditions, conds);
+ else
+ g_warning ("We're confused with sheet condition usage.");
+}
+
+// ----------------------------------------------------------------------------
+
+void
+sheet_conditions_simplify (Sheet *sheet)
+{
+ GHashTableIter hiter;
+ gpointer value;
+ GnmSheetConditionsData *cd = sheet->conditions;
+
+ if (!cd->needs_simplify)
+ return;
+
+ if (debug_sheet_conds)
+ g_printerr ("Optimizing sheet conditions for %s\n",
+ sheet->name_unquoted);
+
+ g_hash_table_iter_init (&hiter, cd->groups);
+ while (g_hash_table_iter_next (&hiter, NULL, &value)) {
+ CSGroup *g = value;
+ simplify_group (g);
+ }
+ cd->needs_simplify = FALSE;
+}
+
+void
+sheet_conditions_dump (Sheet *sheet)
+{
+ GnmSheetConditionsData *cd = sheet->conditions;
+ GHashTableIter hiter;
+ gpointer value;
+
+ g_printerr ("Conditional styling for sheet %s:\n", sheet->name_unquoted);
+ g_hash_table_iter_init (&hiter, cd->groups);
+ while (g_hash_table_iter_next (&hiter, NULL, &value)) {
+ CSGroup *g = value;
+ unsigned ui, ri;
+ GPtrArray const *ga;
+ GnmCellPos const *pos;
+
+ pos = gnm_style_conditions_get_pos (g->conds);
+ g_printerr (" Conditions at %s\n",
+ pos ? cellpos_as_string (pos) : "-");
+ ga = gnm_style_conditions_details (g->conds);
+ for (ui = 0; ui < (ga ? ga->len : 0u); ui++) {
+ GnmStyleCond *sc = g_ptr_array_index (ga, ui);
+ char *s = gnm_style_cond_as_string (sc);
+ g_printerr (" [%d] %s\n", ui, s);
+ g_free (s);
+ }
+
+ g_printerr (" Ranges:\n");
+ for (ri = 0; ri < g->ranges->len; ri++) {
+ GnmRange *r = &g_array_index (g->ranges, GnmRange, ri);
+ g_printerr (" [%d] %s\n", ri, range_as_string (r));
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static CSGroup *
+find_group (GnmSheetConditionsData *cd, GnmStyle *style)
+{
+ GnmStyleConditions const *conds = gnm_style_get_conditions (style);
+ return g_hash_table_lookup (cd->groups, conds);
+}
+
+
+void
+sheet_conditions_add (Sheet *sheet, GnmRange const *r, GnmStyle *style)
+{
+ GnmSheetConditionsData *cd = sheet->conditions;
+ CSGroup *g;
+
+ if (FAST_EXIT && sheet->being_destructed)
+ return;
+
+ g = find_group (cd, style);
+ if (!g) {
+ g = g_new0 (CSGroup, 1);
+ g->dep.base.flags = csgd_get_dep_type ();
+ g->dep.base.sheet = sheet;
+ g->conds = gnm_style_get_conditions (style);
+ g->ranges = g_array_new (FALSE, FALSE, sizeof (GnmRange));
+ g_hash_table_insert (cd->groups, g->conds, g);
+ }
+
+ g_array_append_val (g->ranges, *r);
+ if (g->ranges->len > 1) {
+ if (FAST_LOAD && sheet->workbook->being_loaded)
+ cd->needs_simplify = TRUE;
+ else
+ simplify_group (g);
+ } else
+ update_group (g);
+}
+
+void
+sheet_conditions_remove (Sheet *sheet, GnmRange const *r, GnmStyle *style)
+{
+ GnmSheetConditionsData *cd = sheet->conditions;
+ CSGroup *g;
+ unsigned ri;
+
+ if (FAST_EXIT && sheet->being_destructed) {
+ g_hash_table_remove_all (cd->groups);
+ return;
+ }
+
+ g = find_group (cd, style);
+ if (!g) {
+ g_warning ("Removing conditional style we don't have?");
+ return;
+ }
+
+ for (ri = 0; ri < g->ranges->len; ri++) {
+ GnmRange *r2 = &g_array_index (g->ranges, GnmRange, ri);
+ GnmRange rest[4];
+ int n = 0;
+
+ if (!range_overlap (r, r2))
+ continue;
+
+ if (r->start.col > r2->start.col) {
+ // Keep a section to the left
+ rest[n] = *r2;
+ rest[n].end.col = r->start.col - 1;
+ n++;
+ }
+ if (r->end.col < r2->end.col) {
+ // Keep a section to the right
+ rest[n] = *r2;
+ rest[n].start.col = r->end.col + 1;
+ n++;
+ }
+ if (r->start.row > r2->start.row) {
+ // Keep a section above
+ rest[n] = *r2;
+ rest[n].end.row = r->start.row - 1;
+ n++;
+ }
+ if (r->end.row < r2->end.row) {
+ // Keep a section below
+ rest[n] = *r2;
+ rest[n].start.row = r->end.row + 1;
+ n++;
+ }
+
+ if (n == 0) {
+ g_array_remove_index (g->ranges, ri);
+ ri--; // Counter-act loop increment
+ if (g->ranges->len == 0) {
+ g_hash_table_remove (cd->groups, g->conds);
+ g = NULL;
+ break;
+ }
+ } else {
+ *r2 = rest[0];
+ g_array_append_vals (g->ranges, rest + 1, n - 1);
+ }
+ }
+
+ if (FAST_LOAD && sheet->workbook->being_loaded)
+ cd->needs_simplify = TRUE;
+ else if (g)
+ simplify_group (g);
+}
+
+// ----------------------------------------------------------------------------
+
+static void
+set_group_pos_and_expr (CSGroup *g, const GnmCellPos *pos, GnmExprTop const *texpr)
+{
+ GnmDependent *dep = &g->dep.base;
+
+ if (dependent_is_linked (dep))
+ dependent_unlink (dep);
+ if (texpr != dep->texpr)
+ dependent_set_expr (dep, texpr);
+ g->dep.pos = *pos;
+ if (texpr)
+ dependent_link (dep);
+}
+
+typedef struct {
+ GnmEvalPos epos;
+ GnmExprList *deps;
+ GnmRange const *r;
+ Sheet *sheet;
+} CollectGroupDepsState;
+
+typedef enum {
+ CGD_NO_FLAGS = 0,
+ CGD_NON_SCALAR = 1,
+} CollectGroupDefsFlags;
+
+
+static void
+collect_group_deps_rr (GnmRangeRef const *rr, CollectGroupDepsState *state,
+ CollectGroupDefsFlags flags)
+{
+ GnmRangeRef rr1, rr2;
+ Sheet *a_sheet = eval_sheet (rr->a.sheet, state->sheet);
+ Sheet *b_sheet = eval_sheet (rr->b.sheet, a_sheet);
+ GnmRange r;
+ int col, row;
+ gboolean found = FALSE;
+ int W = range_width (state->r);
+ int H = range_height (state->r);
+
+ if (a_sheet == state->sheet &&
+ rr->a.col_relative && rr->a.col == 0 &&
+ rr->a.row_relative && rr->a.row == 0 &&
+ b_sheet == state->sheet &&
+ rr->b.col_relative && rr->b.col == 0 &&
+ rr->b.row_relative && rr->b.row == 0) {
+ // Ignore references to the cell itself -- the recalc
+ // dependency is enought to update everything.
+ if (debug_sheet_conds)
+ g_printerr ("Self reference\n");
+ return;
+ }
+
+ // Inspiration from value_intersection:
+ gnm_rangeref_normalize (rr, &state->epos, &a_sheet, &b_sheet, &r);
+
+ if (flags & CGD_NON_SCALAR)
+ goto everything;
+
+ if (eval_pos_is_array_context (&state->epos))
+ goto everything; // Potential implicit iteration -- bail
+
+ if (!(a_sheet == b_sheet || b_sheet == NULL))
+ return; // An error
+
+ col = state->epos.eval.col;
+ row = state->epos.eval.row;
+
+ if (range_is_singleton (&r)) {
+ col = r.start.col;
+ row = r.start.row;
+ found = TRUE;
+ } else if (r.start.row == r.end.row &&
+ r.start.col <= col && col + (W - 1) <= r.end.col) {
+ row = r.start.row;
+ found = TRUE;
+ } else if (r.start.col == r.end.col &&
+ r.start.row <= row && row + (H - 1) <= r.end.row) {
+ col = r.start.col;
+ found = TRUE;
+ }
+ if (!found)
+ goto everything;
+
+ // Intersection. The range is equivalent to a single cell
+ gnm_cellref_init (&rr1.a, a_sheet, col, row, FALSE);
+ rr1.b = rr1.a;
+ rr = &rr1;
+
+everything:
+ if (!(a_sheet == b_sheet || b_sheet == NULL)) {
+ if (debug_sheet_conds)
+ g_printerr ("Ignoring 3d reference for conditional style.\n");
+ return;
+ }
+
+ // Ignore wrapping for now.
+ rr2 = *rr;
+ rr2.b.col += W - 1;
+ rr2.b.row += H - 1;
+
+ state->deps = gnm_expr_list_prepend
+ (state->deps,
+ gnm_expr_new_constant
+ (value_new_cellrange_unsafe (&rr2.a, &rr2.b)));
+}
+
+static void
+collect_group_deps (GnmExpr const *expr, CollectGroupDepsState *state,
+ CollectGroupDefsFlags flags)
+{
+ switch (GNM_EXPR_GET_OPER (expr)) {
+ case GNM_EXPR_OP_CONSTANT: {
+ GnmValue const *cst = gnm_expr_get_constant (expr);
+ if (VALUE_IS_CELLRANGE (cst))
+ collect_group_deps_rr (value_get_rangeref (cst),
+ state, flags);
+ return;
+ }
+
+ case GNM_EXPR_OP_NAME: {
+ GnmNamedExpr const *nexpr = gnm_expr_get_name (expr);
+ state->deps = gnm_expr_list_prepend
+ (state->deps,
+ gnm_expr_copy (expr));
+ if (expr_name_is_active (nexpr))
+ collect_group_deps (nexpr->texpr->expr, state, flags);
+ return;
+ }
+
+ case GNM_EXPR_OP_CELLREF: {
+ GnmCellRef const *cr = gnm_expr_get_cellref (expr);
+ GnmRangeRef rr;
+ rr.a = *cr;
+ rr.b = *cr;
+ rr.b.sheet = NULL;
+ collect_group_deps_rr (&rr, state, flags);
+ return;
+ }
+ }
+
+ // Otherwise... descend into subexpressions
+ switch (GNM_EXPR_GET_OPER (expr)) {
+ case GNM_EXPR_OP_RANGE_CTOR:
+ case GNM_EXPR_OP_INTERSECT:
+ collect_group_deps (expr->binary.value_a, state, flags);
+ collect_group_deps (expr->binary.value_b, state, flags);
+ return;
+
+ case GNM_EXPR_OP_ANY_BINARY:
+ if (!eval_pos_is_array_context (&state->epos))
+ flags &= ~CGD_NON_SCALAR;
+ collect_group_deps (expr->binary.value_a, state, flags);
+ collect_group_deps (expr->binary.value_b, state, flags);
+ return;
+
+ case GNM_EXPR_OP_ANY_UNARY:
+ if (!eval_pos_is_array_context (&state->epos))
+ flags &= ~CGD_NON_SCALAR;
+ collect_group_deps (expr->unary.value, state, flags);
+ return;
+
+ case GNM_EXPR_OP_FUNCALL: {
+ GnmExprFunction const *call = &expr->func;
+ GnmFunc const *func = call->func;
+ int i, argc = call->argc;
+ CollectGroupDefsFlags pass = flags & CGD_NO_FLAGS;
+
+ if (gnm_func_get_flags (call->func) & GNM_FUNC_IS_PLACEHOLDER)
+ break;
+
+ for (i = 0; i < argc; i++) {
+ char t = gnm_func_get_arg_type (func, i);
+ CollectGroupDefsFlags extra =
+ (t == 'A' || t == 'r' || t == '?')
+ ? CGD_NON_SCALAR
+ : CGD_NO_FLAGS;
+ collect_group_deps (expr->func.argv[i], state,
+ pass | extra);
+ }
+ return;
+ }
+
+ case GNM_EXPR_OP_SET: {
+ int i, argc = expr->set.argc;
+ for (i = 0; i < argc; i++)
+ collect_group_deps (expr->set.argv[i], state, flags);
+ return;
+ }
+
+ case GNM_EXPR_OP_ARRAY_CORNER:
+ collect_group_deps (expr->array_corner.expr, state,
+ flags | CGD_NON_SCALAR);
+ return;
+
+ default:
+ return;
+ }
+}
+
+
+
+static void
+update_group (CSGroup *g)
+{
+ GnmCellPos const *pos;
+ GnmExprTop const *texpr;
+ GPtrArray const *ga;
+ CollectGroupDepsState state;
+ unsigned ui;
+
+ if (g->ranges->len == 0) {
+ dependent_set_expr (&g->dep.base, NULL);
+ return;
+ }
+
+ pos = &g_array_index (g->ranges, GnmRange, 0).start;
+ gnm_style_conditions_set_pos (g->conds, pos);
+
+ state.deps = NULL;
+ state.sheet = g->dep.base.sheet;
+ ga = gnm_style_conditions_details (g->conds);
+ for (ui = 0; ui < (ga ? ga->len : 0u); ui++) {
+ GnmStyleCond *cond = g_ptr_array_index (ga, ui);
+ unsigned ix;
+ for (ix = 0; ix < G_N_ELEMENTS (cond->deps); ix++) {
+ GnmExprTop const *te = gnm_style_cond_get_expr (cond, ix);
+ if (te) {
+ unsigned ri;
+ eval_pos_init_dep (&state.epos, &cond->deps[ix].base);
+ for (ri = 0; ri < g->ranges->len; ri++) {
+ state.r = &g_array_index (g->ranges, GnmRange, ri);
+ collect_group_deps (te->expr, &state, CGD_NO_FLAGS);
+ }
+ }
+ }
+ }
+ texpr = state.deps
+ ? gnm_expr_top_new (gnm_expr_new_set (state.deps))
+ : gnm_expr_top_new_constant (value_new_error_REF (NULL));
+ set_group_pos_and_expr (g, pos, texpr);
+ gnm_expr_top_unref (texpr);
+}
+
+// ----------------------------------------------------------------------------
+
+static void
+csgd_eval (GnmDependent *dep)
+{
+ // Nothing yet
+}
+
+static GSList *
+csgd_changed (GnmDependent *dep)
+{
+ CSGroupDep *gd = (CSGroupDep *)dep;
+ CSGroup *g = (CSGroup *)gd; // Since the dep is first
+ Sheet *sheet = dep->sheet;
+ unsigned ri;
+
+ for (ri = 0; ri < g->ranges->len; ri++) {
+ GnmRange *r = &g_array_index (g->ranges, GnmRange, ri);
+ // FIXME:
+ // sheet_range_calc_spans ???
+ // or other unrender
+ sheet_queue_redraw_range (sheet, r);
+ }
+
+ return NULL;
+}
+
+static GnmCellPos *
+csgd_pos (GnmDependent const *dep)
+{
+ return &((CSGroupDep *)dep)->pos;
+}
+
+static void
+csgd_debug_name (GnmDependent const *dep, GString *target)
+{
+ g_string_append_printf (target, "CSGroup/%p", (void *)dep);
+}
+
+
+static DEPENDENT_MAKE_TYPE(csgd, .eval = csgd_eval, .changed = csgd_changed, .pos = csgd_pos, .debug_name =
csgd_debug_name)
diff --git a/src/sheet-conditions.h b/src/sheet-conditions.h
new file mode 100644
index 000000000..a2ac40030
--- /dev/null
+++ b/src/sheet-conditions.h
@@ -0,0 +1,23 @@
+#ifndef GNM_SHEET_CONDITIONS_H_
+#define GNM_SHEET_CONDITIONS_H_
+
+#include <gnumeric.h>
+
+G_BEGIN_DECLS
+
+void sheet_conditions_init (Sheet *sheet);
+void sheet_conditions_uninit (Sheet *sheet);
+// resize?
+
+GnmStyleConditions *sheet_conditions_share_conditions_add (GnmStyleConditions *conds);
+void sheet_conditions_share_conditions_remove (GnmStyleConditions *conds);
+
+void sheet_conditions_add (Sheet *sheet, GnmRange const *r, GnmStyle *style);
+void sheet_conditions_remove (Sheet *sheet, GnmRange const *r, GnmStyle *style);
+
+void sheet_conditions_simplify (Sheet *sheet);
+void sheet_conditions_dump (Sheet *sheet);
+
+G_END_DECLS
+
+#endif
diff --git a/src/sheet.c b/src/sheet.c
index fb7f7684a..a0701b744 100644
--- a/src/sheet.c
+++ b/src/sheet.c
@@ -28,6 +28,7 @@
#include <command-context.h>
#include <sheet-control.h>
#include <sheet-style.h>
+#include <sheet-conditions.h>
#include <workbook-priv.h>
#include <workbook-control.h>
#include <workbook-view.h>
@@ -709,6 +710,8 @@ gnm_sheet_constructed (GObject *obj)
range_init_full_sheet (&sheet->priv->unhidden_region, sheet);
sheet_style_init (sheet);
+ sheet_conditions_init (sheet);
+
sheet->deps = gnm_dep_container_new (sheet);
switch (sheet->sheet_type) {
@@ -4964,6 +4967,7 @@ gnm_sheet_finalize (GObject *obj)
}
sheet_style_shutdown (sheet);
+ sheet_conditions_uninit (sheet);
if (sheet->pending_redraw_src) {
g_source_remove (sheet->pending_redraw_src);
diff --git a/src/sheet.h b/src/sheet.h
index 77e0f08b2..00693245f 100644
--- a/src/sheet.h
+++ b/src/sheet.h
@@ -49,6 +49,8 @@ struct _Sheet {
GnmSheetStyleData *style_data; /* See sheet-style.c */
+ GnmSheetConditionsData *conditions; // See sheet-conditions.c
+
ColRowCollection cols, rows;
GHashTable *cell_hash; /* The cells in hashed format */
diff --git a/src/style-conditions.c b/src/style-conditions.c
index c14b6eec3..3f8f969c2 100644
--- a/src/style-conditions.c
+++ b/src/style-conditions.c
@@ -28,7 +28,6 @@
#include <cell.h>
#include <value.h>
#include <sheet.h>
-#include <parse-util.h>
#include <gsf/gsf-impl-utils.h>
#include <string.h>
#include <func.h>
@@ -52,6 +51,12 @@ debug_style_conds (void)
return debug;
}
+// ----------------------------------------------------------------------------
+
+static guint gscd_get_dep_type (void);
+
+// ----------------------------------------------------------------------------
+
static unsigned
gnm_style_cond_op_operands (GnmStyleCondOp op)
{
@@ -84,7 +89,6 @@ gnm_style_cond_op_operands (GnmStyleCondOp op)
g_assert_not_reached ();
}
-
/**
* gnm_style_cond_is_valid:
* @cond: #GnmStyleCond
@@ -107,7 +111,7 @@ gnm_style_cond_is_valid (GnmStyleCond const *cond)
N = gnm_style_cond_op_operands (cond->op);
for (ui = 0; ui < G_N_ELEMENTS (cond->deps); ui++) {
gboolean need = (ui < N);
- gboolean have = (dependent_managed_get_expr (&cond->deps[ui]) != NULL);
+ gboolean have = (cond->deps[ui].base.texpr != NULL);
if (have != need)
return FALSE;
}
@@ -125,8 +129,10 @@ gnm_style_cond_new (GnmStyleCondOp op, Sheet *sheet)
res = g_new0 (GnmStyleCond, 1);
res->op = op;
- for (ui = 0; ui < 2; ui++)
- dependent_managed_init (&res->deps[ui], sheet);
+ for (ui = 0; ui < 2; ui++) {
+ res->deps[ui].base.flags = gscd_get_dep_type ();
+ res->deps[ui].base.sheet = sheet;
+ }
return res;
}
@@ -148,7 +154,7 @@ gnm_style_cond_dup_to (GnmStyleCond const *src, Sheet *sheet)
dst = gnm_style_cond_new (src->op, sheet);
gnm_style_cond_set_overlay (dst, src->overlay);
for (ui = 0; ui < 2; ui++)
- gnm_style_cond_set_expr (dst, dependent_managed_get_expr (&src->deps[ui]), ui);
+ gnm_style_cond_set_expr (dst, src->deps[ui].base.texpr, ui);
return dst;
}
@@ -222,7 +228,7 @@ gnm_style_cond_get_expr (GnmStyleCond const *cond, unsigned idx)
g_return_val_if_fail (cond != NULL, NULL);
g_return_val_if_fail (idx < G_N_ELEMENTS (cond->deps), NULL);
- return dependent_managed_get_expr (&cond->deps[idx]);
+ return cond->deps[idx].base.texpr;
}
void
@@ -233,7 +239,9 @@ gnm_style_cond_set_expr (GnmStyleCond *cond,
g_return_if_fail (cond != NULL);
g_return_if_fail (idx < G_N_ELEMENTS (cond->deps));
- dependent_managed_set_expr (&cond->deps[idx], texpr);
+ dependent_set_expr (&cond->deps[idx].base, texpr);
+ if (texpr)
+ dependent_link (&cond->deps[idx].base);
}
void
@@ -559,21 +567,24 @@ case_insensitive_has_fix (GnmValue const *vs, GnmValue const *vp,
static gboolean
-gnm_style_cond_eval (GnmStyleCond const *cond, GnmValue const *cv,
+gnm_style_cond_eval (GnmStyleCond *cond, GnmValue const *cv,
GnmEvalPos const *ep)
{
gboolean negate = FALSE;
gboolean res;
GnmValue *val0 = NULL;
GnmValue *val1 = NULL;
+ GnmEvalPos epos = *ep;
switch (gnm_style_cond_op_operands (cond->op)) {
case 2:
- val1 = gnm_expr_top_eval (cond->deps[1].base.texpr, ep,
+ epos.dep = &cond->deps[1].base;
+ val1 = gnm_expr_top_eval (cond->deps[1].base.texpr, &epos,
GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
/* Fall through */
case 1:
- val0 = gnm_expr_top_eval (cond->deps[0].base.texpr, ep,
+ epos.dep = &cond->deps[0].base;
+ val0 = gnm_expr_top_eval (cond->deps[0].base.texpr, &epos,
GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
/* Fall through */
case 0:
@@ -687,6 +698,91 @@ gnm_style_cond_equal (GnmStyleCond const *ca, GnmStyleCond const *cb,
return TRUE;
}
+static void
+gnm_style_cond_set_pos (GnmStyleCond *sc, GnmCellPos const *pos)
+{
+ unsigned oi, N;
+
+ N = gnm_style_cond_op_operands (sc->op);
+ for (oi = 0; oi < N; oi++) {
+ gboolean qlink = dependent_is_linked (&sc->deps[oi].base);
+ if (qlink)
+ dependent_unlink (&sc->deps[oi].base);
+ sc->deps[oi].pos = *pos;
+ if (qlink)
+ dependent_link (&sc->deps[oi].base);
+ }
+}
+
+// For debugging purposes
+char *
+gnm_style_cond_as_string (GnmStyleCond const *cond)
+{
+ unsigned oi, N;
+ static const char * const ops[] = {
+ "between", "not-between",
+ "equal", "not-equal",
+ "greater-than", "less-then",
+ "greater-than-or-equal", "less-than-or-equal",
+ "is-true",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "contains", "does-not-contain",
+ "begins-with", "does-not-begin-with",
+ "end-with", "does-not-end-with",
+ "is-error", "is-not-error",
+ "contains-blank", "does-not-contain-blank"
+ };
+ GString *str = g_string_new (ops[cond->op]);
+ Sheet *sheet = gnm_style_cond_get_sheet (cond);
+ GnmConventions const *convs = sheet_get_conventions (sheet);
+
+ N = gnm_style_cond_op_operands (cond->op);
+ for (oi = 0; oi < N; oi++) {
+ char *s;
+ GnmParsePos pp;
+
+ parse_pos_init_dep (&pp, &cond->deps[oi].base);
+ s = gnm_expr_top_as_string (gnm_style_cond_get_expr (cond, oi),
+ &pp,
+ convs);
+ g_string_append_c (str, ' ');
+ g_string_append (str, s);
+ g_free (s);
+ }
+ return g_string_free (str, FALSE);
+}
+
+// ----------------------------------------------------------------------------
+
+static void
+gscd_eval (GnmDependent *dep)
+{
+ // Nothing yet
+}
+
+static GSList *
+gscd_changed (GnmDependent *dep)
+{
+ GnmStyleCondDep const *scd = (GnmStyleCondDep const *)dep;
+ return scd->dep_cont ? g_slist_prepend (NULL, scd->dep_cont) : NULL;
+}
+
+static GnmCellPos *
+gscd_pos (GnmDependent const *dep)
+{
+ return &((GnmStyleCondDep *)dep)->pos;
+}
+
+static void
+gscd_debug_name (GnmDependent const *dep, GString *target)
+{
+ g_string_append_printf (target, "StyleCondDep/%p", (void *)dep);
+}
+
+
+static DEPENDENT_MAKE_TYPE(gscd, .eval = gscd_eval, .changed = gscd_changed, .pos = gscd_pos, .debug_name =
gscd_debug_name)
+
+// ----------------------------------------------------------------------------
static void
gnm_style_conditions_finalize (GObject *obj)
@@ -1001,7 +1097,7 @@ gnm_style_conditions_eval (GnmStyleConditions const *sc, GnmEvalPos const *ep)
}
for (i = 0 ; i < conds->len ; i++) {
- GnmStyleCond const *cond = g_ptr_array_index (conds, i);
+ GnmStyleCond *cond = g_ptr_array_index (conds, i);
gboolean use_this = gnm_style_cond_eval (cond, cv, ep);
if (use_this) {
@@ -1018,3 +1114,54 @@ gnm_style_conditions_eval (GnmStyleConditions const *sc, GnmEvalPos const *ep)
value_release (cv);
return -1;
}
+
+
+/**
+ * gnm_style_conditions_set_pos:
+ * @sc: #GnmStyleConditions
+ * @pos: new position
+ *
+ * Sets the position of @sc, i.e., the position at which relative addresses
+ * in the conditions will be evaluated.
+ **/
+void
+gnm_style_conditions_set_pos (GnmStyleConditions *sc,
+ GnmCellPos const *pos)
+{
+ GPtrArray const *ga;
+ unsigned ui;
+
+ g_return_if_fail (sc != NULL);
+
+ ga = gnm_style_conditions_details (sc);
+ for (ui = 0; ui < (ga ? ga->len : 0u); ui++) {
+ GnmStyleCond *cond = g_ptr_array_index (ga, ui);
+ gnm_style_cond_set_pos (cond, pos);
+ }
+}
+
+/**
+ * gnm_style_conditions_get_pos:
+ * @sc: #GnmStyleConditions
+ *
+ * Returns: (transfer none) (nullable): The position at which relative
+ * addresses in the conditions will be evaluated. This may be %NULL if
+ * no conditions require a position.
+ **/
+GnmCellPos const *
+gnm_style_conditions_get_pos (GnmStyleConditions const *sc)
+{
+ GPtrArray const *ga;
+ unsigned ui;
+
+ g_return_val_if_fail (sc != NULL, NULL);
+
+ ga = gnm_style_conditions_details (sc);
+ for (ui = 0; ui < (ga ? ga->len : 0u); ui++) {
+ GnmStyleCond *cond = g_ptr_array_index (ga, ui);
+ int N = gnm_style_cond_op_operands (cond->op);
+ if (N > 0)
+ return dependent_pos (&cond->deps[0].base);
+ }
+ return NULL;
+}
diff --git a/src/style-conditions.h b/src/style-conditions.h
index 5297c9e52..92d839153 100644
--- a/src/style-conditions.h
+++ b/src/style-conditions.h
@@ -6,7 +6,8 @@
G_BEGIN_DECLS
-/* This is persisted directly in .gnumeric files, DO NOT REMOVE OR REORDER */
+// The values here are persisted directly in .gnumeric files as numbers.
+// DO NOT REMOVE OR REORDER ANYTHING
typedef enum {
/* Cell Value */
GNM_STYLE_COND_BETWEEN,
@@ -36,9 +37,15 @@ typedef enum {
GNM_STYLE_COND_NOT_CONTAINS_BLANKS
} GnmStyleCondOp;
+typedef struct {
+ GnmDependent base;
+ GnmCellPos pos;
+ GnmDependent *dep_cont;
+} GnmStyleCondDep;
+
typedef struct {
GnmStyle *overlay;
- GnmDepManaged deps[2];
+ GnmStyleCondDep deps[2];
GnmStyleCondOp op;
} GnmStyleCond;
@@ -60,6 +67,10 @@ GnmExprTop const *gnm_style_cond_get_alternate_expr (GnmStyleCond const *cond);
void gnm_style_cond_canonicalize (GnmStyleCond *cond);
Sheet *gnm_style_cond_get_sheet (GnmStyleCond const *cond);
+char *gnm_style_cond_as_string (GnmStyleCond const *cond);
+
+
+
GType gnm_style_conditions_get_type (void);
GnmStyleConditions *gnm_style_conditions_new (Sheet *sheet);
@@ -84,6 +95,10 @@ gboolean gnm_style_conditions_equal (GnmStyleConditions const *sca,
GnmStyleConditions const *scb,
gboolean relax_sheet);
+GnmCellPos const *gnm_style_conditions_get_pos (GnmStyleConditions const *sc);
+void gnm_style_conditions_set_pos (GnmStyleConditions *sc,
+ GnmCellPos const *pos);
+
G_END_DECLS
#endif /* _GNM_STYLE_CONDITIONS_H_ */
diff --git a/src/wbc-gtk.c b/src/wbc-gtk.c
index d73dff2b9..d3cc16947 100644
--- a/src/wbc-gtk.c
+++ b/src/wbc-gtk.c
@@ -33,6 +33,7 @@
#include <sheet-private.h>
#include <sheet-view.h>
#include <sheet-style.h>
+#include <sheet-conditions.h>
#include <sheet-filter.h>
#include <commands.h>
#include <dependent.h>
@@ -2240,6 +2241,12 @@ cb_workbook_debug_info (WBCGtk *wbcg)
workbook_optimize_style (wb);
}
+ if (gnm_debug_flag ("sheet-conditions")) {
+ WORKBOOK_FOREACH_SHEET(wb, sheet, {
+ sheet_conditions_dump (sheet);
+ });
+ }
+
if (gnm_debug_flag ("name-collections")) {
gnm_named_expr_collection_dump (wb->names, "workbook");
WORKBOOK_FOREACH_SHEET(wb, sheet, {
@@ -2847,6 +2854,7 @@ wbc_gtk_create_edit_area (WBCGtk *wbcg)
gnm_debug_flag ("deps") ||
gnm_debug_flag ("expr-sharer") ||
gnm_debug_flag ("style-optimize") ||
+ gnm_debug_flag ("sheet-conditions") ||
gnm_debug_flag ("name-collections")) {
g_signal_connect_swapped (debug_button,
"clicked", G_CALLBACK (cb_workbook_debug_info),
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]