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:
211 * @cond: #GnmStyleCond
214 * Returns: (transfer none): the #GnmExprTop for the @idx'th condition.
217 gnm_style_cond_get_expr (GnmStyleCond
const *cond
, unsigned idx
)
219 g_return_val_if_fail (cond
!= NULL
, NULL
);
220 g_return_val_if_fail (idx
< G_N_ELEMENTS (cond
->deps
), NULL
);
222 return cond
->deps
[idx
].texpr
;
226 gnm_style_cond_set_expr (GnmStyleCond
*cond
,
227 GnmExprTop
const *texpr
,
230 g_return_if_fail (cond
!= NULL
);
231 g_return_if_fail (idx
< G_N_ELEMENTS (cond
->deps
));
233 dependent_managed_set_expr (&cond
->deps
[idx
], texpr
);
237 gnm_style_cond_set_overlay (GnmStyleCond
*cond
, GnmStyle
*overlay
)
239 g_return_if_fail (cond
!= NULL
);
242 gnm_style_ref (overlay
);
244 gnm_style_unref (cond
->overlay
);
245 cond
->overlay
= overlay
;
248 static GnmExpr
const *
249 generate_end_match (const char *endfunc
, gboolean force
, gboolean negate
,
250 GnmExprTop
const *sexpr
, GnmCellRef
*cr
)
252 GnmValue
const *v
= gnm_expr_get_constant (sexpr
->expr
);
253 GnmExpr
const *len_expr
;
255 if (v
&& VALUE_IS_STRING (v
)) {
256 int len
= g_utf8_strlen (value_peek_string (v
), -1);
257 len_expr
= gnm_expr_new_constant (value_new_int (len
));
260 * This is imperfect because the expression gets
263 len_expr
= gnm_expr_new_funcall1
264 (gnm_func_lookup_or_add_placeholder ("LEN"),
265 gnm_expr_copy (sexpr
->expr
));
269 return gnm_expr_new_binary
270 (gnm_expr_new_funcall2
271 (gnm_func_lookup_or_add_placeholder (endfunc
),
272 gnm_expr_new_cellref (cr
),
274 negate
? GNM_EXPR_OP_NOT_EQUAL
: GNM_EXPR_OP_EQUAL
,
275 gnm_expr_copy (sexpr
->expr
));
280 * gnm_style_cond_get_alternate_expr:
283 * Returns: (transfer full) (allow-none): An custom expression that can be
284 * used in place of @cond.
287 gnm_style_cond_get_alternate_expr (GnmStyleCond
const *cond
)
291 gboolean negate
= FALSE
;
292 GnmExprTop
const *sexpr
= NULL
;
294 g_return_val_if_fail (cond
!= NULL
, NULL
);
296 gnm_cellref_init (&self
, NULL
, 0, 0, TRUE
);
298 if (gnm_style_cond_op_operands (cond
->op
) > 0) {
299 sexpr
= gnm_style_cond_get_expr (cond
, 0);
305 case GNM_STYLE_COND_NOT_CONTAINS_ERR
:
306 negate
= TRUE
; /* ...and fall through */
307 case GNM_STYLE_COND_CONTAINS_ERR
:
308 expr
= gnm_expr_new_funcall1
309 (gnm_func_lookup_or_add_placeholder ("ISERROR"),
310 gnm_expr_new_cellref (&self
));
313 case GNM_STYLE_COND_CONTAINS_STR
:
314 negate
= TRUE
; /* ...and fall through */
315 case GNM_STYLE_COND_NOT_CONTAINS_STR
:
316 expr
= gnm_expr_new_funcall1
317 (gnm_func_lookup_or_add_placeholder ("ISERROR"),
318 gnm_expr_new_funcall2
319 (gnm_func_lookup_or_add_placeholder ("FIND"),
320 gnm_expr_copy (sexpr
->expr
),
321 gnm_expr_new_cellref (&self
)));
324 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS
:
325 negate
= TRUE
; /* ...and fall through */
326 case GNM_STYLE_COND_CONTAINS_BLANKS
:
327 /* This means blanks-only */
329 expr
= gnm_expr_new_binary
330 (gnm_expr_new_funcall1
331 (gnm_func_lookup_or_add_placeholder ("LEN"),
332 gnm_expr_new_funcall1
333 (gnm_func_lookup_or_add_placeholder ("TRIM"),
334 gnm_expr_new_cellref (&self
))),
335 negate
? GNM_EXPR_OP_GT
: GNM_EXPR_OP_EQUAL
,
336 gnm_expr_new_constant (value_new_int (0)));
340 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR
:
341 negate
= TRUE
; /* ...and fall through */
342 case GNM_STYLE_COND_BEGINS_WITH_STR
:
344 * We are constrained by using only Excel functions and not
345 * evaluating the needle more than once. We cannot fulfill
346 * that and end up computing the needle twice.
348 expr
= generate_end_match ("LEFT", TRUE
, negate
, sexpr
, &self
);
352 case GNM_STYLE_COND_NOT_ENDS_WITH_STR
:
353 negate
= TRUE
; /* ...and fall through */
354 case GNM_STYLE_COND_ENDS_WITH_STR
:
356 * We are constrained by using only Excel functions and not
357 * evaluating the needle more than once. We cannot fulfill
358 * that and end up computing the needle twice.
360 expr
= generate_end_match ("RIGHT", TRUE
, negate
, sexpr
, &self
);
369 expr
= gnm_expr_new_funcall1
370 (gnm_func_lookup_or_add_placeholder ("NOT"), expr
);
372 return gnm_expr_top_new (expr
);
376 isself (GnmExpr
const *expr
)
378 GnmCellRef
const *cr
= gnm_expr_get_cellref (expr
);
382 cr
->col
== 0 && cr
->row
== 0 &&
383 cr
->col_relative
&& cr
->row_relative
);
386 static GnmExprTop
const *
387 decode_end_match (const char *endfunc
, GnmExpr
const *expr
, gboolean
*negated
)
389 GnmExpr
const *needle
;
390 GnmExpr
const *expr2
;
392 *negated
= (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_NOT_EQUAL
);
394 if ((GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_EQUAL
||
395 GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_NOT_EQUAL
) &&
396 (needle
= expr
->binary
.value_b
) &&
397 (expr2
= expr
->binary
.value_a
) &&
398 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
399 expr2
->func
.argc
== 2 &&
400 expr2
->func
.func
== gnm_func_lookup_or_add_placeholder (endfunc
) &&
401 isself (expr2
->func
.argv
[0])) {
402 GnmExpr
const *len_expr
= expr2
->func
.argv
[1];
403 GnmValue
const *v
, *vl
;
405 if (GNM_EXPR_GET_OPER (len_expr
) == GNM_EXPR_OP_FUNCALL
&&
406 len_expr
->func
.argc
== 1 &&
407 len_expr
->func
.func
== gnm_func_lookup_or_add_placeholder ("LEN") &&
408 gnm_expr_equal (len_expr
->func
.argv
[0], needle
))
409 return gnm_expr_top_new (gnm_expr_copy (needle
));
411 if ((v
= gnm_expr_get_constant (needle
)) &&
412 VALUE_IS_STRING (v
) &&
413 (vl
= gnm_expr_get_constant (len_expr
)) &&
414 VALUE_IS_NUMBER (vl
) &&
415 value_get_as_float (vl
) == g_utf8_strlen (value_peek_string (v
), -1))
416 return gnm_expr_top_new (gnm_expr_copy (needle
));
423 * gnm_style_cond_canonicalize:
426 * Turns a custom condition into a more specific one, i.e., reverses the
427 * effect of using gnm_style_cond_get_alternate_expr. Leaves the condition
428 * alone if it is not recognized.
431 gnm_style_cond_canonicalize (GnmStyleCond
*cond
)
433 GnmExpr
const *expr
, *expr2
;
434 GnmExprTop
const *texpr
;
436 gboolean negate
= FALSE
;
437 gboolean match_negated
;
438 GnmFunc
const *iserror
;
439 GnmFunc
const *iferror
;
441 GnmStyleCondOp newop
= GNM_STYLE_COND_CUSTOM
;
443 g_return_if_fail (cond
!= NULL
);
445 if (cond
->op
!= GNM_STYLE_COND_CUSTOM
)
448 texpr
= gnm_style_cond_get_expr (cond
, 0);
454 if (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_FUNCALL
&&
455 expr
->func
.argc
== 1 &&
456 expr
->func
.func
== gnm_func_lookup_or_add_placeholder ("NOT")) {
458 expr
= expr
->func
.argv
[0];
461 iserror
= gnm_func_lookup_or_add_placeholder ("ISERROR");
462 iferror
= gnm_func_lookup_or_add_placeholder ("IFERROR");
463 find
= gnm_func_lookup_or_add_placeholder ("FIND");
465 if (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_FUNCALL
&&
466 expr
->func
.argc
== 1 && expr
->func
.func
== iserror
&&
467 isself (expr
->func
.argv
[0])) {
469 ? GNM_STYLE_COND_NOT_CONTAINS_ERR
470 : GNM_STYLE_COND_CONTAINS_ERR
;
471 } else if (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_FUNCALL
&&
472 expr
->func
.argc
== 1 && expr
->func
.func
== iserror
&&
473 (expr2
= expr
->func
.argv
[0]) &&
474 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
475 expr2
->func
.argc
== 2 && expr2
->func
.func
== find
&&
476 isself (expr2
->func
.argv
[1])) {
477 texpr
= gnm_expr_top_new (gnm_expr_copy (expr2
->func
.argv
[0]));
479 ? GNM_STYLE_COND_CONTAINS_STR
480 : GNM_STYLE_COND_NOT_CONTAINS_STR
;
481 } else if ((GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_EQUAL
||
482 GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_GT
) &&
483 (v
= gnm_expr_get_constant (expr
->binary
.value_b
)) &&
484 VALUE_IS_FLOAT (v
) && value_get_as_float (v
) == 0 &&
485 (expr2
= expr
->binary
.value_a
) &&
486 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
487 expr2
->func
.argc
== 1 &&
488 expr2
->func
.func
== gnm_func_lookup_or_add_placeholder ("LEN") &&
489 (expr2
= expr2
->func
.argv
[0]) &&
490 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
491 expr2
->func
.argc
== 1 &&
492 expr2
->func
.func
== gnm_func_lookup_or_add_placeholder ("TRIM") &&
493 isself (expr2
->func
.argv
[0])) {
494 if (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_GT
)
498 ? GNM_STYLE_COND_NOT_CONTAINS_BLANKS
499 : GNM_STYLE_COND_CONTAINS_BLANKS
;
500 } else if (GNM_EXPR_GET_OPER (expr
) == GNM_EXPR_OP_EQUAL
&&
501 (v
= gnm_expr_get_constant (expr
->binary
.value_b
)) &&
502 VALUE_IS_FLOAT (v
) && value_get_as_float (v
) == 1 &&
503 (expr2
= expr
->binary
.value_a
) &&
504 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
505 expr2
->func
.argc
== 2 && expr2
->func
.func
== iferror
&&
506 (v
= gnm_expr_get_constant (expr2
->func
.argv
[1])) &&
507 VALUE_IS_FLOAT (v
) && value_get_as_float (v
) != 1 &&
508 (expr2
= expr2
->func
.argv
[0]) &&
509 GNM_EXPR_GET_OPER (expr2
) == GNM_EXPR_OP_FUNCALL
&&
510 expr2
->func
.argc
== 2 && expr2
->func
.func
== find
&&
511 isself (expr2
->func
.argv
[1])) {
512 texpr
= gnm_expr_top_new (gnm_expr_copy (expr2
->func
.argv
[0]));
514 ? GNM_STYLE_COND_NOT_BEGINS_WITH_STR
515 : GNM_STYLE_COND_BEGINS_WITH_STR
;
516 } else if ((texpr
= decode_end_match ("LEFT", expr
, &match_negated
))) {
517 newop
= (negate
^ match_negated
)
518 ? GNM_STYLE_COND_NOT_BEGINS_WITH_STR
519 : GNM_STYLE_COND_BEGINS_WITH_STR
;
520 } else if ((texpr
= decode_end_match ("RIGHT", expr
, &match_negated
))) {
521 newop
= (negate
^ match_negated
)
522 ? GNM_STYLE_COND_NOT_ENDS_WITH_STR
523 : GNM_STYLE_COND_ENDS_WITH_STR
;
526 if (newop
!= GNM_STYLE_COND_CUSTOM
) {
527 gnm_style_cond_set_expr (cond
, texpr
, 0);
529 gnm_expr_top_unref (texpr
);
535 case_insensitive_has_fix (GnmValue
const *vs
, GnmValue
const *vp
,
538 size_t plen
= g_utf8_strlen (value_peek_string (vp
), -1);
539 const char *s
= value_peek_string (vs
);
540 size_t slen
= g_utf8_strlen (s
, -1);
547 vs2
= value_new_string_nocopy
549 ? g_strndup (s
, g_utf8_offset_to_pointer (s
, plen
) - s
)
550 : g_strdup (g_utf8_offset_to_pointer (s
, slen
- plen
)));
551 res
= (value_compare (vs2
, vp
, FALSE
) == IS_EQUAL
);
559 gnm_style_cond_eval (GnmStyleCond
const *cond
, GnmValue
const *cv
,
560 GnmEvalPos
const *ep
)
562 gboolean negate
= FALSE
;
564 GnmValue
*val0
= NULL
;
565 GnmValue
*val1
= NULL
;
567 switch (gnm_style_cond_op_operands (cond
->op
)) {
569 val1
= gnm_expr_top_eval (cond
->deps
[1].texpr
, ep
,
570 GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
573 val0
= gnm_expr_top_eval (cond
->deps
[0].texpr
, ep
,
574 GNM_EXPR_EVAL_SCALAR_NON_EMPTY
);
579 g_assert_not_reached ();
583 case GNM_STYLE_COND_NOT_EQUAL
:
584 negate
= TRUE
; /* ...and fall through */
585 case GNM_STYLE_COND_EQUAL
:
586 res
= value_compare (cv
, val0
, FALSE
) == IS_EQUAL
;
589 case GNM_STYLE_COND_LTE
:
590 negate
= TRUE
; /* ...and fall through */
591 case GNM_STYLE_COND_GT
:
592 res
= value_compare (cv
, val0
, FALSE
) == IS_GREATER
;
595 case GNM_STYLE_COND_GTE
:
596 negate
= TRUE
; /* ...and fall through */
597 case GNM_STYLE_COND_LT
:
598 res
= value_compare (cv
, val0
, FALSE
) == IS_LESS
;
601 case GNM_STYLE_COND_NOT_BETWEEN
:
602 negate
= TRUE
; /* ...and fall through */
603 case GNM_STYLE_COND_BETWEEN
:
604 res
= !(value_compare (cv
, val0
, FALSE
) == IS_LESS
||
605 value_compare (cv
, val1
, FALSE
) == IS_GREATER
);
608 case GNM_STYLE_COND_NOT_CONTAINS_ERR
:
609 negate
= TRUE
; /* ...and fall through */
610 case GNM_STYLE_COND_CONTAINS_ERR
:
611 res
= cv
&& VALUE_IS_ERROR (cv
);
614 case GNM_STYLE_COND_NOT_CONTAINS_BLANKS
:
615 negate
= TRUE
; /* ...and fall through */
616 case GNM_STYLE_COND_CONTAINS_BLANKS
: {
617 const char *s
= cv
? value_peek_string (cv
) : "";
619 gunichar uc
= g_utf8_get_char (s
);
620 if (!g_unichar_isspace (uc
))
622 s
= g_utf8_next_char (s
);
628 case GNM_STYLE_COND_NOT_CONTAINS_STR
:
629 negate
= TRUE
; /* ...and fall through */
630 case GNM_STYLE_COND_CONTAINS_STR
:
632 gnm_excel_search_impl (value_peek_string (val0
),
633 value_peek_string (cv
),
637 case GNM_STYLE_COND_NOT_BEGINS_WITH_STR
:
638 negate
= TRUE
; /* ...and fall through */
639 case GNM_STYLE_COND_BEGINS_WITH_STR
:
640 res
= (cv
&& case_insensitive_has_fix (cv
, val0
, TRUE
));
643 case GNM_STYLE_COND_NOT_ENDS_WITH_STR
:
644 negate
= TRUE
; /* ...and fall through */
645 case GNM_STYLE_COND_ENDS_WITH_STR
:
646 res
= (cv
&& case_insensitive_has_fix (cv
, val0
, FALSE
));
649 case GNM_STYLE_COND_CUSTOM
:
650 res
= value_get_as_bool (val0
, NULL
);
654 g_assert_not_reached ();
657 value_release (val0
);
658 value_release (val1
);
660 return negate
? !res
: res
;
664 gnm_style_cond_equal (GnmStyleCond
const *ca
, GnmStyleCond
const *cb
,
665 gboolean relax_sheet
)
669 if (ca
->op
!= cb
->op
)
672 if (!gnm_style_equal (ca
->overlay
, cb
->overlay
))
675 N
= gnm_style_cond_op_operands (ca
->op
);
676 for (oi
= 0; oi
< N
; oi
++) {
677 if (!relax_sheet
&& ca
->deps
[oi
].sheet
!= cb
->deps
[oi
].sheet
)
679 if (!gnm_expr_top_equal (ca
->deps
[oi
].texpr
,
689 gnm_style_conditions_finalize (GObject
*obj
)
691 GnmStyleConditions
*sc
= (GnmStyleConditions
*)obj
;
693 while (sc
->conditions
)
694 gnm_style_conditions_delete (sc
, sc
->conditions
->len
- 1);
695 G_OBJECT_CLASS (parent_class
)->finalize (obj
);
699 gnm_style_conditions_init (GnmStyleConditions
*sc
)
701 sc
->conditions
= NULL
;
705 gnm_style_conditions_class_init (GObjectClass
*gobject_class
)
707 parent_class
= g_type_class_peek_parent (gobject_class
);
708 gobject_class
->finalize
= gnm_style_conditions_finalize
;
710 GSF_CLASS (GnmStyleConditions
, gnm_style_conditions
,
711 gnm_style_conditions_class_init
, gnm_style_conditions_init
,
715 * gnm_style_conditions_new:
718 * Convenience tool to create a #GnmStyleCondition. Straight g_object_new
721 * Returns: (transfer full): a #GnmStyleConditions
724 gnm_style_conditions_new (Sheet
*sheet
)
726 GnmStyleConditions
*res
;
727 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
729 res
= g_object_new (gnm_style_conditions_get_type (), NULL
);
735 * gnm_style_conditions_dup:
736 * @sc: (nullable): the #GnmStyleConditions to duplicate.
738 * Returns: (transfer full) (nullable): the duplicated #GnmStyleConditions.
741 gnm_style_conditions_dup (GnmStyleConditions
const *sc
)
743 GnmStyleConditions
*dup
;
748 dup
= gnm_style_conditions_new (gnm_style_conditions_get_sheet (sc
));
749 ga
= gnm_style_conditions_details (sc
);
752 GPtrArray
*ga_dup
= g_ptr_array_sized_new (ga
->len
);
753 for (i
= 0; i
< ga
->len
; i
++) {
754 GnmStyleCond
*cond
= g_ptr_array_index (ga
, i
);
755 g_ptr_array_add (ga_dup
, gnm_style_cond_dup (cond
));
757 dup
->conditions
= ga_dup
;
762 #define MIX(H) do { \
763 H *= G_GUINT64_CONSTANT(123456789012345); \
768 gnm_style_conditions_hash (GnmStyleConditions
const *sc
)
775 * Note: this hash must not depend on the expressions stored
776 * in ->deps. And probably not on the sheet either.
779 g_return_val_if_fail (sc
!= NULL
, 0u);
781 ga
= gnm_style_conditions_details (sc
);
782 for (ui
= 0; ui
< (ga
? ga
->len
: 0u); ui
++) {
783 GnmStyleCond
*cond
= g_ptr_array_index (ga
, ui
);
785 hash
^= gnm_style_hash_XL (cond
->overlay
);
797 * gnm_style_conditions_equal:
798 * @sca: first #GnmStyleConditions to compare.
799 * @scb: second #GnmStyleConditions to compare.
800 * @relax_sheet: if %TRUE, ignore differences solely caused by being linked into different sheets.
802 * Returns: %TRUE if the conditions are equal.
805 gnm_style_conditions_equal (GnmStyleConditions
const *sca
,
806 GnmStyleConditions
const *scb
,
807 gboolean relax_sheet
)
809 GPtrArray
const *ga
, *gb
;
812 g_return_val_if_fail (sca
!= NULL
, FALSE
);
813 g_return_val_if_fail (scb
!= NULL
, FALSE
);
815 if (!relax_sheet
&& sca
->sheet
!= scb
->sheet
)
818 ga
= gnm_style_conditions_details (sca
);
819 gb
= gnm_style_conditions_details (scb
);
822 if (ga
->len
!= gb
->len
)
825 for (ui
= 0; ui
< ga
->len
; ui
++) {
826 GnmStyleCond
const *ca
= g_ptr_array_index (ga
, ui
);
827 GnmStyleCond
const *cb
= g_ptr_array_index (gb
, ui
);
828 if (!gnm_style_cond_equal (ca
, cb
, relax_sheet
))
837 * gnm_style_conditions_get_sheet:
838 * @sc: #GnmStyleConditions
840 * Returns: (transfer none): the #Sheet.
843 gnm_style_conditions_get_sheet (GnmStyleConditions
const *sc
)
845 g_return_val_if_fail (sc
!= NULL
, NULL
);
850 gnm_style_conditions_set_sheet (GnmStyleConditions
*sc
, Sheet
*sheet
)
855 g_return_if_fail (sc
!= NULL
);
856 g_return_if_fail (IS_SHEET (sheet
));
859 ga
= gnm_style_conditions_details (sc
);
860 for (ui
= 0; ga
&& ui
< ga
->len
; ui
++) {
861 GnmStyleCond
*cond
= g_ptr_array_index (ga
, ui
);
862 gnm_style_cond_set_sheet (cond
, sheet
);
868 * gnm_style_conditions_details:
869 * @sc: #GnmStyleConditions
871 * Returns: (element-type GnmStyleCond) (transfer none): style details.
874 gnm_style_conditions_details (GnmStyleConditions
const *sc
)
876 g_return_val_if_fail (sc
!= NULL
, NULL
);
878 return sc
->conditions
;
882 * gnm_style_conditions_insert:
883 * @sc: #GnmStyleConditions
884 * @cond: #GnmStyleCond
887 * Insert @cond before @pos (append if @pos < 0).
890 gnm_style_conditions_insert (GnmStyleConditions
*sc
,
891 GnmStyleCond
const *cond_
, int pos
)
895 g_return_if_fail (sc
!= NULL
);
896 g_return_if_fail (cond_
!= NULL
);
898 g_return_if_fail (gnm_style_cond_is_valid (cond_
));
900 g_return_if_fail (gnm_style_conditions_get_sheet (sc
) ==
901 gnm_style_cond_get_sheet (cond_
));
903 if (sc
->conditions
== NULL
)
904 sc
->conditions
= g_ptr_array_new ();
906 cond
= gnm_style_cond_dup (cond_
);
907 g_ptr_array_add (sc
->conditions
, cond
);
911 for (i
= sc
->conditions
->len
- 1;
914 g_ptr_array_index (sc
->conditions
, i
) =
915 g_ptr_array_index (sc
->conditions
, i
- 1);
916 g_ptr_array_index (sc
->conditions
, pos
) = cond
;
921 gnm_style_conditions_delete (GnmStyleConditions
*sc
, guint pos
)
923 g_return_if_fail (sc
!= NULL
);
924 g_return_if_fail (sc
->conditions
!= NULL
);
925 g_return_if_fail (sc
->conditions
->len
> pos
);
927 gnm_style_cond_free (g_ptr_array_index (sc
->conditions
, pos
));
928 if (sc
->conditions
->len
<= 1) {
929 g_ptr_array_free (sc
->conditions
, TRUE
);
930 sc
->conditions
= NULL
;
932 g_ptr_array_remove_index (sc
->conditions
, pos
);
937 * gnm_style_conditions_overlay:
938 * @sc: #GnmStyleConditions
941 * Returns: (element-type GnmStyle) (transfer full): an array of #GnmStyle.
944 gnm_style_conditions_overlay (GnmStyleConditions
const *sc
,
945 GnmStyle
const *base
)
950 g_return_val_if_fail (sc
!= NULL
, NULL
);
951 g_return_val_if_fail (sc
->conditions
!= NULL
, NULL
);
953 res
= g_ptr_array_sized_new (sc
->conditions
->len
);
954 for (i
= 0 ; i
< sc
->conditions
->len
; i
++) {
955 GnmStyleCond
const *cond
=
956 g_ptr_array_index (sc
->conditions
, i
);
957 GnmStyle
const *overlay
= cond
->overlay
;
958 GnmStyle
*merge
= gnm_style_new_merged (base
, overlay
);
959 /* We only draw a background colour if the pattern != 0 */
960 if (gnm_style_get_pattern (merge
) == 0 &&
961 gnm_style_is_element_set (overlay
, MSTYLE_COLOR_BACK
) &&
962 !gnm_style_is_element_set (overlay
, MSTYLE_PATTERN
))
963 gnm_style_set_pattern (merge
, 1);
964 g_ptr_array_add (res
, merge
);
970 * gnm_style_conditions_eval:
971 * @sc: #GnmStyleConditions
974 * Returns: the condition to use or -1 if none match.
977 gnm_style_conditions_eval (GnmStyleConditions
const *sc
, GnmEvalPos
const *ep
)
980 GPtrArray
const *conds
;
981 GnmCell
const *cell
= sheet_cell_get (ep
->sheet
, ep
->eval
.col
, ep
->eval
.row
);
982 GnmValue
const *cv
= cell
? cell
->value
: NULL
;
984 g_return_val_if_fail (sc
!= NULL
, -1);
985 g_return_val_if_fail (sc
->conditions
!= NULL
, -1);
987 conds
= sc
->conditions
;
989 if (debug_style_conds ()) {
991 parse_pos_init_evalpos (&pp
, ep
);
993 g_printerr ("Evaluating conditions %p at %s with %d clauses\n",
995 parsepos_as_string (&pp
),
999 for (i
= 0 ; i
< conds
->len
; i
++) {
1000 GnmStyleCond
const *cond
= g_ptr_array_index (conds
, i
);
1001 gboolean use_this
= gnm_style_cond_eval (cond
, cv
, ep
);
1004 if (debug_style_conds ())
1005 g_printerr (" Using clause %d\n", i
);
1010 if (debug_style_conds ())
1011 g_printerr (" No matching clauses\n");