4 * Copyright (C) 2005-2007 Jody Goldberg (jody@gnome.org)
5 * Copyright (C) 2013-2014 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 "style-conditions.h"
27 #include "expr-impl.h"
31 #include <parse-util.h>
32 #include <gsf/gsf-impl-utils.h>
37 typedef GObjectClass GnmStyleConditionsClass
;
38 struct _GnmStyleConditions
{
40 GPtrArray
*conditions
;
44 static GObjectClass
*parent_class
;
47 debug_style_conds (void)
49 static int debug
= -1;
51 debug
= gnm_debug_flag ("style-conds");
56 gnm_style_cond_op_operands (GnmStyleCondOp op
)
59 case GNM_STYLE_COND_BETWEEN
:
60 case GNM_STYLE_COND_NOT_BETWEEN
:
63 case GNM_STYLE_COND_EQUAL
:
64 case GNM_STYLE_COND_NOT_EQUAL
:
65 case GNM_STYLE_COND_GT
:
66 case GNM_STYLE_COND_LT
:
67 case GNM_STYLE_COND_GTE
:
68 case GNM_STYLE_COND_LTE
:
69 case GNM_STYLE_COND_CUSTOM
:
70 case GNM_STYLE_COND_CONTAINS_STR
:
71 case GNM_STYLE_COND_NOT_CONTAINS_STR
:
72 case GNM_STYLE_COND_BEGINS_WITH_STR
:
73 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR
:
74 case GNM_STYLE_COND_ENDS_WITH_STR
:
75 case GNM_STYLE_COND_NOT_ENDS_WITH_STR
:
78 case GNM_STYLE_COND_CONTAINS_ERR
:
79 case GNM_STYLE_COND_NOT_CONTAINS_ERR
:
80 case GNM_STYLE_COND_CONTAINS_BLANKS
:
81 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS
:
84 g_assert_not_reached ();
89 * gnm_style_cond_is_valid:
90 * @cond: #GnmStyleCond
92 * Returns: TRUE if @cond is in a reasonable state
95 gnm_style_cond_is_valid (GnmStyleCond
const *cond
)
99 g_return_val_if_fail (cond
!= NULL
, FALSE
);
101 if (cond
->overlay
== NULL
)
103 if ((unsigned)cond
->op
> (unsigned)GNM_STYLE_COND_NOT_CONTAINS_BLANKS
||
104 (cond
->op
> GNM_STYLE_COND_CUSTOM
&& cond
->op
< GNM_STYLE_COND_CONTAINS_STR
))
107 N
= gnm_style_cond_op_operands (cond
->op
);
108 for (ui
= 0; ui
< G_N_ELEMENTS (cond
->deps
); ui
++) {
109 gboolean need
= (ui
< N
);
110 gboolean have
= (cond
->deps
[ui
].texpr
!= NULL
);
119 gnm_style_cond_new (GnmStyleCondOp op
, Sheet
*sheet
)
124 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
126 res
= g_new0 (GnmStyleCond
, 1);
128 for (ui
= 0; ui
< 2; ui
++)
129 dependent_managed_init (&res
->deps
[ui
], sheet
);
134 * gnm_style_cond_dup:
135 * @src: #GnmStyleCond
137 * Returns: (transfer full): the newly allocated #GnmStyleCond.
140 gnm_style_cond_dup (GnmStyleCond
const *src
)
145 g_return_val_if_fail (src
!= NULL
, NULL
);
147 dst
= gnm_style_cond_new (src
->op
, gnm_style_cond_get_sheet (src
));
148 gnm_style_cond_set_overlay (dst
, src
->overlay
);
149 for (ui
= 0; ui
< 2; ui
++)
150 gnm_style_cond_set_expr (dst
, src
->deps
[ui
].texpr
, ui
);
156 gnm_style_cond_free (GnmStyleCond
*cond
)
160 g_return_if_fail (cond
!= NULL
);
162 /* Be very careful: this is called for invalid conditions too */
164 gnm_style_unref (cond
->overlay
);
165 for (ui
= 0; ui
< 2; ui
++)
166 gnm_style_cond_set_expr (cond
, NULL
, ui
);
172 gnm_style_cond_get_type (void)
177 t
= g_boxed_type_register_static ("GnmStyleCond",
178 (GBoxedCopyFunc
)gnm_style_cond_dup
,
179 (GBoxedFreeFunc
)gnm_style_cond_free
);
185 * gnm_style_cond_get_sheet:
186 * @cond: #GnmStyleCond
188 * Returns: (transfer none): the #Sheet.
191 gnm_style_cond_get_sheet (GnmStyleCond
const *cond
)
193 g_return_val_if_fail (cond
!= NULL
, NULL
);
194 return cond
->deps
[0].sheet
;
198 gnm_style_cond_set_sheet (GnmStyleCond
*cond
, Sheet
*sheet
)
202 g_return_if_fail (cond
!= NULL
);
203 g_return_if_fail (IS_SHEET (sheet
));
205 for (ui
= 0; ui
< 2; ui
++)
206 dependent_managed_set_sheet (&cond
->deps
[ui
], sheet
);
210 gnm_style_cond_get_expr (GnmStyleCond
const *cond
, unsigned idx
)
212 g_return_val_if_fail (cond
!= NULL
, NULL
);
213 g_return_val_if_fail (idx
< G_N_ELEMENTS (cond
->deps
), NULL
);
215 return cond
->deps
[idx
].texpr
;
219 gnm_style_cond_set_expr (GnmStyleCond
*cond
,
220 GnmExprTop
const *texpr
,
223 g_return_if_fail (cond
!= NULL
);
224 g_return_if_fail (idx
< G_N_ELEMENTS (cond
->deps
));
226 dependent_managed_set_expr (&cond
->deps
[idx
], texpr
);
230 gnm_style_cond_set_overlay (GnmStyleCond
*cond
, GnmStyle
*overlay
)
232 g_return_if_fail (cond
!= NULL
);
235 gnm_style_ref (overlay
);
237 gnm_style_unref (cond
->overlay
);
238 cond
->overlay
= overlay
;
241 static GnmExpr
const *
242 generate_end_match (const char *endfunc
, gboolean force
, gboolean negate
,
243 GnmExprTop
const *sexpr
, GnmCellRef
*cr
)
245 GnmValue
const *v
= gnm_expr_get_constant (sexpr
->expr
);
246 GnmExpr
const *len_expr
;
248 if (v
&& VALUE_IS_STRING (v
)) {
249 int len
= g_utf8_strlen (value_peek_string (v
), -1);
250 len_expr
= gnm_expr_new_constant (value_new_int (len
));
253 * This is imperfect because the expression gets
256 len_expr
= gnm_expr_new_funcall1
257 (gnm_func_lookup_or_add_placeholder ("LEN"),
258 gnm_expr_copy (sexpr
->expr
));
262 return gnm_expr_new_binary
263 (gnm_expr_new_funcall2
264 (gnm_func_lookup_or_add_placeholder (endfunc
),
265 gnm_expr_new_cellref (cr
),
267 negate
? GNM_EXPR_OP_NOT_EQUAL
: GNM_EXPR_OP_EQUAL
,
268 gnm_expr_copy (sexpr
->expr
));
273 * gnm_style_cond_get_alternate_expr:
276 * Returns: (transfer full) (allow-none): An custom expression that can be
277 * used in place of @cond.
280 gnm_style_cond_get_alternate_expr (GnmStyleCond
const *cond
)
284 gboolean negate
= FALSE
;
285 GnmExprTop
const *sexpr
= NULL
;
287 g_return_val_if_fail (cond
!= NULL
, NULL
);
289 gnm_cellref_init (&self
, NULL
, 0, 0, TRUE
);
291 if (gnm_style_cond_op_operands (cond
->op
) > 0) {
292 sexpr
= gnm_style_cond_get_expr (cond
, 0);
298 case GNM_STYLE_COND_NOT_CONTAINS_ERR
:
299 negate
= TRUE
; /* ...and fall through */
300 case GNM_STYLE_COND_CONTAINS_ERR
:
301 expr
= gnm_expr_new_funcall1
302 (gnm_func_lookup_or_add_placeholder ("ISERROR"),
303 gnm_expr_new_cellref (&self
));
306 case GNM_STYLE_COND_CONTAINS_STR
:
307 negate
= TRUE
; /* ...and fall through */
308 case GNM_STYLE_COND_NOT_CONTAINS_STR
:
309 expr
= gnm_expr_new_funcall1
310 (gnm_func_lookup_or_add_placeholder ("ISERROR"),
311 gnm_expr_new_funcall2
312 (gnm_func_lookup_or_add_placeholder ("FIND"),
313 gnm_expr_copy (sexpr
->expr
),
314 gnm_expr_new_cellref (&self
)));
317 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS
:
318 negate
= TRUE
; /* ...and fall through */
319 case GNM_STYLE_COND_CONTAINS_BLANKS
:
320 /* This means blanks-only */
322 expr
= gnm_expr_new_binary
323 (gnm_expr_new_funcall1
324 (gnm_func_lookup_or_add_placeholder ("LEN"),
325 gnm_expr_new_funcall1
326 (gnm_func_lookup_or_add_placeholder ("TRIM"),
327 gnm_expr_new_cellref (&self
))),
328 negate
? GNM_EXPR_OP_GT
: GNM_EXPR_OP_EQUAL
,
329 gnm_expr_new_constant (value_new_int (0)));
333 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR
:
334 negate
= TRUE
; /* ...and fall through */
335 case GNM_STYLE_COND_BEGINS_WITH_STR
:
337 * We are constrained by using only Excel functions and not
338 * evaluating the needle more than once. We cannot fulfill
339 * that and end up computing the needle twice.
341 expr
= generate_end_match ("LEFT", TRUE
, negate
, sexpr
, &self
);
345 case GNM_STYLE_COND_NOT_ENDS_WITH_STR
:
346 negate
= TRUE
; /* ...and fall through */
347 case GNM_STYLE_COND_ENDS_WITH_STR
:
349 * We are constrained by using only Excel functions and not
350 * evaluating the needle more than once. We cannot fulfill
351 * that and end up computing the needle twice.
353 expr
= generate_end_match ("RIGHT", TRUE
, negate
, sexpr
, &self
);
362 expr
= gnm_expr_new_funcall1
363 (gnm_func_lookup_or_add_placeholder ("NOT"), expr
);
365 return gnm_expr_top_new (expr
);
369 isself (GnmExpr
const *expr
)
371 GnmCellRef
const *cr
= gnm_expr_get_cellref (expr
);
375 cr
->col
== 0 && cr
->row
== 0 &&
376 cr
->col_relative
&& cr
->row_relative
);
379 static GnmExprTop
const *
380 decode_end_match (const char *endfunc
, GnmExpr
const *expr
, gboolean
*negated
)
382 GnmExpr
const *needle
;
383 GnmExpr
const *expr2
;
385 *negated
= (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_NOT_EQUAL
);
387 if ((GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_EQUAL
||
388 GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_NOT_EQUAL
) &&
389 (needle
= expr
->binary
.value_b
) &&
390 (expr2
= expr
->binary
.value_a
) &&
391 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
392 expr2
->func
.argc
== 2 &&
393 expr2
->func
.func
== gnm_func_lookup_or_add_placeholder (endfunc
) &&
394 isself (expr2
->func
.argv
[0])) {
395 GnmExpr
const *len_expr
= expr2
->func
.argv
[1];
396 GnmValue
const *v
, *vl
;
398 if (GNM_EXPR_GET_OPER (len_expr
) == GNM_EXPR_OP_FUNCALL
&&
399 len_expr
->func
.argc
== 1 &&
400 len_expr
->func
.func
== gnm_func_lookup_or_add_placeholder ("LEN") &&
401 gnm_expr_equal (len_expr
->func
.argv
[0], needle
))
402 return gnm_expr_top_new (gnm_expr_copy (needle
));
404 if ((v
= gnm_expr_get_constant (needle
)) &&
405 VALUE_IS_STRING (v
) &&
406 (vl
= gnm_expr_get_constant (len_expr
)) &&
407 VALUE_IS_NUMBER (vl
) &&
408 value_get_as_float (vl
) == g_utf8_strlen (value_peek_string (v
), -1))
409 return gnm_expr_top_new (gnm_expr_copy (needle
));
416 * gnm_style_cond_canonicalize:
419 * Turns a custom condition into a more specific one, i.e., reverses the
420 * effect of using gnm_style_cond_get_alternate_expr. Leaves the condition
421 * alone if it is not recognized.
424 gnm_style_cond_canonicalize (GnmStyleCond
*cond
)
426 GnmExpr
const *expr
, *expr2
;
427 GnmExprTop
const *texpr
;
429 gboolean negate
= FALSE
;
430 gboolean match_negated
;
431 GnmFunc
const *iserror
;
432 GnmFunc
const *iferror
;
434 GnmStyleCondOp newop
= GNM_STYLE_COND_CUSTOM
;
436 g_return_if_fail (cond
!= NULL
);
438 if (cond
->op
!= GNM_STYLE_COND_CUSTOM
)
441 texpr
= gnm_style_cond_get_expr (cond
, 0);
447 if (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_FUNCALL
&&
448 expr
->func
.argc
== 1 &&
449 expr
->func
.func
== gnm_func_lookup_or_add_placeholder ("NOT")) {
451 expr
= expr
->func
.argv
[0];
454 iserror
= gnm_func_lookup_or_add_placeholder ("ISERROR");
455 iferror
= gnm_func_lookup_or_add_placeholder ("IFERROR");
456 find
= gnm_func_lookup_or_add_placeholder ("FIND");
458 if (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_FUNCALL
&&
459 expr
->func
.argc
== 1 && expr
->func
.func
== iserror
&&
460 isself (expr
->func
.argv
[0])) {
462 ? GNM_STYLE_COND_NOT_CONTAINS_ERR
463 : GNM_STYLE_COND_CONTAINS_ERR
;
464 } else if (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_FUNCALL
&&
465 expr
->func
.argc
== 1 && expr
->func
.func
== iserror
&&
466 (expr2
= expr
->func
.argv
[0]) &&
467 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
468 expr2
->func
.argc
== 2 && expr2
->func
.func
== find
&&
469 isself (expr2
->func
.argv
[1])) {
470 texpr
= gnm_expr_top_new (gnm_expr_copy (expr2
->func
.argv
[0]));
472 ? GNM_STYLE_COND_CONTAINS_STR
473 : GNM_STYLE_COND_NOT_CONTAINS_STR
;
474 } else if ((GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_EQUAL
||
475 GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_GT
) &&
476 (v
= gnm_expr_get_constant (expr
->binary
.value_b
)) &&
477 VALUE_IS_FLOAT (v
) && value_get_as_float (v
) == 0 &&
478 (expr2
= expr
->binary
.value_a
) &&
479 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
480 expr2
->func
.argc
== 1 &&
481 expr2
->func
.func
== gnm_func_lookup_or_add_placeholder ("LEN") &&
482 (expr2
= expr2
->func
.argv
[0]) &&
483 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
484 expr2
->func
.argc
== 1 &&
485 expr2
->func
.func
== gnm_func_lookup_or_add_placeholder ("TRIM") &&
486 isself (expr2
->func
.argv
[0])) {
487 if (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_GT
)
491 ? GNM_STYLE_COND_NOT_CONTAINS_BLANKS
492 : GNM_STYLE_COND_CONTAINS_BLANKS
;
493 } else if (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_EQUAL
&&
494 (v
= gnm_expr_get_constant (expr
->binary
.value_b
)) &&
495 VALUE_IS_FLOAT (v
) && value_get_as_float (v
) == 1 &&
496 (expr2
= expr
->binary
.value_a
) &&
497 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
498 expr2
->func
.argc
== 2 && expr2
->func
.func
== iferror
&&
499 (v
= gnm_expr_get_constant (expr2
->func
.argv
[1])) &&
500 VALUE_IS_FLOAT (v
) && value_get_as_float (v
) != 1 &&
501 (expr2
= expr2
->func
.argv
[0]) &&
502 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
503 expr2
->func
.argc
== 2 && expr2
->func
.func
== find
&&
504 isself (expr2
->func
.argv
[1])) {
505 texpr
= gnm_expr_top_new (gnm_expr_copy (expr2
->func
.argv
[0]));
507 ? GNM_STYLE_COND_NOT_BEGINS_WITH_STR
508 : GNM_STYLE_COND_BEGINS_WITH_STR
;
509 } else if ((texpr
= decode_end_match ("LEFT", expr
, &match_negated
))) {
510 newop
= (negate
^ match_negated
)
511 ? GNM_STYLE_COND_NOT_BEGINS_WITH_STR
512 : GNM_STYLE_COND_BEGINS_WITH_STR
;
513 } else if ((texpr
= decode_end_match ("RIGHT", expr
, &match_negated
))) {
514 newop
= (negate
^ match_negated
)
515 ? GNM_STYLE_COND_NOT_ENDS_WITH_STR
516 : GNM_STYLE_COND_ENDS_WITH_STR
;
519 if (newop
!= GNM_STYLE_COND_CUSTOM
) {
520 gnm_style_cond_set_expr (cond
, texpr
, 0);
522 gnm_expr_top_unref (texpr
);
528 case_insensitive_has_fix (GnmValue
const *vs
, GnmValue
const *vp
,
531 size_t plen
= g_utf8_strlen (value_peek_string (vp
), -1);
532 const char *s
= value_peek_string (vs
);
533 size_t slen
= g_utf8_strlen (s
, -1);
540 vs2
= value_new_string_nocopy
542 ? g_strndup (s
, g_utf8_offset_to_pointer (s
, plen
) - s
)
543 : g_strdup (g_utf8_offset_to_pointer (s
, slen
- plen
)));
544 res
= (value_compare (vs2
, vp
, FALSE
) == IS_EQUAL
);
552 gnm_style_cond_eval (GnmStyleCond
const *cond
, GnmValue
const *cv
,
553 GnmEvalPos
const *ep
)
555 gboolean negate
= FALSE
;
557 GnmValue
*val0
= NULL
;
558 GnmValue
*val1
= NULL
;
560 switch (gnm_style_cond_op_operands (cond
->op
)) {
562 val1
= gnm_expr_top_eval (cond
->deps
[1].texpr
, ep
,
563 GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
566 val0
= gnm_expr_top_eval (cond
->deps
[0].texpr
, ep
,
567 GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
572 g_assert_not_reached ();
576 case GNM_STYLE_COND_NOT_EQUAL
:
577 negate
= TRUE
; /* ...and fall through */
578 case GNM_STYLE_COND_EQUAL
:
579 res
= value_compare (cv
, val0
, FALSE
) == IS_EQUAL
;
582 case GNM_STYLE_COND_LTE
:
583 negate
= TRUE
; /* ...and fall through */
584 case GNM_STYLE_COND_GT
:
585 res
= value_compare (cv
, val0
, FALSE
) == IS_GREATER
;
588 case GNM_STYLE_COND_GTE
:
589 negate
= TRUE
; /* ...and fall through */
590 case GNM_STYLE_COND_LT
:
591 res
= value_compare (cv
, val0
, FALSE
) == IS_LESS
;
594 case GNM_STYLE_COND_NOT_BETWEEN
:
595 negate
= TRUE
; /* ...and fall through */
596 case GNM_STYLE_COND_BETWEEN
:
597 res
= !(value_compare (cv
, val0
, FALSE
) == IS_LESS
||
598 value_compare (cv
, val1
, FALSE
) == IS_GREATER
);
601 case GNM_STYLE_COND_NOT_CONTAINS_ERR
:
602 negate
= TRUE
; /* ...and fall through */
603 case GNM_STYLE_COND_CONTAINS_ERR
:
604 res
= cv
&& VALUE_IS_ERROR (cv
);
607 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS
:
608 negate
= TRUE
; /* ...and fall through */
609 case GNM_STYLE_COND_CONTAINS_BLANKS
: {
610 const char *s
= cv
? value_peek_string (cv
) : "";
612 gunichar uc
= g_utf8_get_char (s
);
613 if (!g_unichar_isspace (uc
))
615 s
= g_utf8_next_char (s
);
621 case GNM_STYLE_COND_NOT_CONTAINS_STR
:
622 negate
= TRUE
; /* ...and fall through */
623 case GNM_STYLE_COND_CONTAINS_STR
:
625 gnm_excel_search_impl (value_peek_string (val0
),
626 value_peek_string (cv
),
630 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR
:
631 negate
= TRUE
; /* ...and fall through */
632 case GNM_STYLE_COND_BEGINS_WITH_STR
:
633 res
= (cv
&& case_insensitive_has_fix (cv
, val0
, TRUE
));
636 case GNM_STYLE_COND_NOT_ENDS_WITH_STR
:
637 negate
= TRUE
; /* ...and fall through */
638 case GNM_STYLE_COND_ENDS_WITH_STR
:
639 res
= (cv
&& case_insensitive_has_fix (cv
, val0
, FALSE
));
642 case GNM_STYLE_COND_CUSTOM
:
643 res
= value_get_as_bool (val0
, NULL
);
647 g_assert_not_reached ();
650 value_release (val0
);
651 value_release (val1
);
653 return negate
? !res
: res
;
657 gnm_style_cond_equal (GnmStyleCond
const *ca
, GnmStyleCond
const *cb
,
658 gboolean relax_sheet
)
662 if (ca
->op
!= cb
->op
)
665 if (!gnm_style_equal (ca
->overlay
, cb
->overlay
))
668 N
= gnm_style_cond_op_operands (ca
->op
);
669 for (oi
= 0; oi
< N
; oi
++) {
670 if (!relax_sheet
&& ca
->deps
[oi
].sheet
!= cb
->deps
[oi
].sheet
)
672 if (!gnm_expr_top_equal (ca
->deps
[oi
].texpr
,
682 gnm_style_conditions_finalize (GObject
*obj
)
684 GnmStyleConditions
*sc
= (GnmStyleConditions
*)obj
;
686 while (sc
->conditions
)
687 gnm_style_conditions_delete (sc
, sc
->conditions
->len
- 1);
688 G_OBJECT_CLASS (parent_class
)->finalize (obj
);
692 gnm_style_conditions_init (GnmStyleConditions
*sc
)
694 sc
->conditions
= NULL
;
698 gnm_style_conditions_class_init (GObjectClass
*gobject_class
)
700 parent_class
= g_type_class_peek_parent (gobject_class
);
701 gobject_class
->finalize
= gnm_style_conditions_finalize
;
703 GSF_CLASS (GnmStyleConditions
, gnm_style_conditions
,
704 gnm_style_conditions_class_init
, gnm_style_conditions_init
,
708 * gnm_style_conditions_new:
710 * Convenience tool to create a GnmStyleCondition. Straight g_object_new
713 * Returns a GnmStyleConditions that the caller is responsible for.
716 gnm_style_conditions_new (Sheet
*sheet
)
718 GnmStyleConditions
*res
;
719 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
721 res
= g_object_new (gnm_style_conditions_get_type (), NULL
);
727 * gnm_style_conditions_dup:
728 * @sc: the #GnmStyleConditions to duplicate.
730 * Returns: (transfer full): the duplicated #GnmStyleConditions.
733 gnm_style_conditions_dup (GnmStyleConditions
const *sc
)
735 GnmStyleConditions
*dup
;
740 dup
= gnm_style_conditions_new (gnm_style_conditions_get_sheet (sc
));
741 ga
= gnm_style_conditions_details (sc
);
744 GPtrArray
*ga_dup
= g_ptr_array_sized_new (ga
->len
);
745 for (i
= 0; i
< ga
->len
; i
++) {
746 GnmStyleCond
*cond
= g_ptr_array_index (ga
, i
);
747 g_ptr_array_add (ga_dup
, gnm_style_cond_dup (cond
));
749 dup
->conditions
= ga_dup
;
754 #define MIX(H) do { \
755 H *= G_GUINT64_CONSTANT(123456789012345); \
760 gnm_style_conditions_hash (GnmStyleConditions
const *sc
)
767 * Note: this hash must not depend on the expressions stored
768 * in ->deps. And probably not on the sheet either.
771 g_return_val_if_fail (sc
!= NULL
, 0u);
773 ga
= gnm_style_conditions_details (sc
);
774 for (ui
= 0; ui
< (ga
? ga
->len
: 0u); ui
++) {
775 GnmStyleCond
*cond
= g_ptr_array_index (ga
, ui
);
777 hash
^= gnm_style_hash_XL (cond
->overlay
);
789 * gnm_style_conditions_equal:
790 * @sca: first #GnmStyleConditions to compare.
791 * @scb: second #GnmStyleConditions to compare.
792 * @relax_sheet: if %TRUE, ignore differences solely caused by being linked into different sheets.
794 * Returns: %TRUE if the conditions are equal.
797 gnm_style_conditions_equal (GnmStyleConditions
const *sca
,
798 GnmStyleConditions
const *scb
,
799 gboolean relax_sheet
)
801 GPtrArray
const *ga
, *gb
;
804 g_return_val_if_fail (sca
!= NULL
, FALSE
);
805 g_return_val_if_fail (scb
!= NULL
, FALSE
);
807 if (!relax_sheet
&& sca
->sheet
!= scb
->sheet
)
810 ga
= gnm_style_conditions_details (sca
);
811 gb
= gnm_style_conditions_details (scb
);
814 if (ga
->len
!= gb
->len
)
817 for (ui
= 0; ui
< ga
->len
; ui
++) {
818 GnmStyleCond
const *ca
= g_ptr_array_index (ga
, ui
);
819 GnmStyleCond
const *cb
= g_ptr_array_index (gb
, ui
);
820 if (!gnm_style_cond_equal (ca
, cb
, relax_sheet
))
829 * gnm_style_conditions_get_sheet:
830 * @sc: #GnmStyleConditions
832 * Returns: (transfer none): the #Sheet.
835 gnm_style_conditions_get_sheet (GnmStyleConditions
const *sc
)
837 g_return_val_if_fail (sc
!= NULL
, NULL
);
842 gnm_style_conditions_set_sheet (GnmStyleConditions
*sc
, Sheet
*sheet
)
847 g_return_if_fail (sc
!= NULL
);
848 g_return_if_fail (IS_SHEET (sheet
));
851 ga
= gnm_style_conditions_details (sc
);
852 for (ui
= 0; ga
&& ui
< ga
->len
; ui
++) {
853 GnmStyleCond
*cond
= g_ptr_array_index (ga
, ui
);
854 gnm_style_cond_set_sheet (cond
, sheet
);
860 * gnm_style_conditions_details:
861 * @sc: #GnmStyleConditions
863 * Returns: (element-type GnmStyleCond) (transfer none): style details.
866 gnm_style_conditions_details (GnmStyleConditions
const *sc
)
868 g_return_val_if_fail (sc
!= NULL
, NULL
);
870 return sc
->conditions
;
874 * gnm_style_conditions_insert:
875 * @sc: #GnmStyleConditions
876 * @cond: #GnmStyleCond
879 * Insert @cond before @pos (append if @pos < 0).
882 gnm_style_conditions_insert (GnmStyleConditions
*sc
,
883 GnmStyleCond
const *cond_
, int pos
)
887 g_return_if_fail (sc
!= NULL
);
888 g_return_if_fail (cond_
!= NULL
);
890 g_return_if_fail (gnm_style_cond_is_valid (cond_
));
892 g_return_if_fail (gnm_style_conditions_get_sheet (sc
) ==
893 gnm_style_cond_get_sheet (cond_
));
895 if (sc
->conditions
== NULL
)
896 sc
->conditions
= g_ptr_array_new ();
898 cond
= gnm_style_cond_dup (cond_
);
899 g_ptr_array_add (sc
->conditions
, cond
);
903 for (i
= sc
->conditions
->len
- 1;
906 g_ptr_array_index (sc
->conditions
, i
) =
907 g_ptr_array_index (sc
->conditions
, i
- 1);
908 g_ptr_array_index (sc
->conditions
, pos
) = cond
;
913 gnm_style_conditions_delete (GnmStyleConditions
*sc
, guint pos
)
915 g_return_if_fail (sc
!= NULL
);
916 g_return_if_fail (sc
->conditions
!= NULL
);
917 g_return_if_fail (sc
->conditions
->len
> pos
);
919 gnm_style_cond_free (g_ptr_array_index (sc
->conditions
, pos
));
920 if (sc
->conditions
->len
<= 1) {
921 g_ptr_array_free (sc
->conditions
, TRUE
);
922 sc
->conditions
= NULL
;
924 g_ptr_array_remove_index (sc
->conditions
, pos
);
929 * gnm_style_conditions_overlay:
930 * @sc: #GnmStyleConditions
933 * Returns: (element-type GnmStyle) (transfer full): an array of #GnmStyle.
936 gnm_style_conditions_overlay (GnmStyleConditions
const *sc
,
937 GnmStyle
const *base
)
942 g_return_val_if_fail (sc
!= NULL
, NULL
);
943 g_return_val_if_fail (sc
->conditions
!= NULL
, NULL
);
945 res
= g_ptr_array_sized_new (sc
->conditions
->len
);
946 for (i
= 0 ; i
< sc
->conditions
->len
; i
++) {
947 GnmStyleCond
const *cond
=
948 g_ptr_array_index (sc
->conditions
, i
);
949 GnmStyle
const *overlay
= cond
->overlay
;
950 GnmStyle
*merge
= gnm_style_new_merged (base
, overlay
);
951 /* We only draw a background colour if the pattern != 0 */
952 if (gnm_style_get_pattern (merge
) == 0 &&
953 gnm_style_is_element_set (overlay
, MSTYLE_COLOR_BACK
) &&
954 !gnm_style_is_element_set (overlay
, MSTYLE_PATTERN
))
955 gnm_style_set_pattern (merge
, 1);
956 g_ptr_array_add (res
, merge
);
962 * gnm_style_conditions_eval:
963 * @sc: #GnmStyleConditions
966 * Returns: the condition to use or -1 if none match.
969 gnm_style_conditions_eval (GnmStyleConditions
const *sc
, GnmEvalPos
const *ep
)
972 GPtrArray
const *conds
;
973 GnmCell
const *cell
= sheet_cell_get (ep
->sheet
, ep
->eval
.col
, ep
->eval
.row
);
974 GnmValue
const *cv
= cell
? cell
->value
: NULL
;
976 g_return_val_if_fail (sc
!= NULL
, -1);
977 g_return_val_if_fail (sc
->conditions
!= NULL
, -1);
979 conds
= sc
->conditions
;
981 if (debug_style_conds ()) {
983 parse_pos_init_evalpos (&pp
, ep
);
985 g_printerr ("Evaluating conditions %p at %s with %d clauses\n",
987 parsepos_as_string (&pp
),
991 for (i
= 0 ; i
< conds
->len
; i
++) {
992 GnmStyleCond
const *cond
= g_ptr_array_index (conds
, i
);
993 gboolean use_this
= gnm_style_cond_eval (cond
, cv
, ep
);
996 if (debug_style_conds ())
997 g_printerr (" Using clause %d\n", i
);
1002 if (debug_style_conds ())
1003 g_printerr (" No matching clauses\n");