Update Spanish translation
[gnumeric.git] / src / style-conditions.c
blob9f8ec167c038a56269e99a001bbb917d690a38d9
1 /*
2 * style-conditions.c:
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
20 * USA
22 #include <gnumeric-config.h>
23 #include <gnumeric.h>
24 #include <style-conditions.h>
25 #include <mstyle.h>
26 #include <expr.h>
27 #include <expr-impl.h>
28 #include <cell.h>
29 #include <value.h>
30 #include <sheet.h>
31 #include <parse-util.h>
32 #include <gsf/gsf-impl-utils.h>
33 #include <string.h>
34 #include <func.h>
35 #include <gutils.h>
37 typedef GObjectClass GnmStyleConditionsClass;
38 struct _GnmStyleConditions {
39 GObject base;
40 GPtrArray *conditions;
41 Sheet *sheet;
44 static GObjectClass *parent_class;
46 static gboolean
47 debug_style_conds (void)
49 static int debug = -1;
50 if (debug < 0)
51 debug = gnm_debug_flag ("style-conds");
52 return debug;
55 static unsigned
56 gnm_style_cond_op_operands (GnmStyleCondOp op)
58 switch (op) {
59 case GNM_STYLE_COND_BETWEEN:
60 case GNM_STYLE_COND_NOT_BETWEEN:
61 return 2;
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:
76 return 1;
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:
82 return 0;
84 g_assert_not_reached ();
88 /**
89 * gnm_style_cond_is_valid:
90 * @cond: #GnmStyleCond
92 * Returns: %TRUE if @cond is in a reasonable state
93 **/
94 gboolean
95 gnm_style_cond_is_valid (GnmStyleCond const *cond)
97 unsigned ui, N;
99 g_return_val_if_fail (cond != NULL, FALSE);
101 if (cond->overlay == NULL)
102 return FALSE;
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))
105 return FALSE;
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);
111 if (have != need)
112 return FALSE;
115 return TRUE;
118 GnmStyleCond *
119 gnm_style_cond_new (GnmStyleCondOp op, Sheet *sheet)
121 GnmStyleCond *res;
122 unsigned ui;
124 g_return_val_if_fail (IS_SHEET (sheet), NULL);
126 res = g_new0 (GnmStyleCond, 1);
127 res->op = op;
128 for (ui = 0; ui < 2; ui++)
129 dependent_managed_init (&res->deps[ui], sheet);
130 return res;
134 * gnm_style_cond_dup:
135 * @src: #GnmStyleCond
137 * Returns: (transfer full): the newly allocated #GnmStyleCond.
139 GnmStyleCond *
140 gnm_style_cond_dup (GnmStyleCond const *src)
142 GnmStyleCond *dst;
143 unsigned ui;
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);
152 return dst;
155 void
156 gnm_style_cond_free (GnmStyleCond *cond)
158 unsigned ui;
160 g_return_if_fail (cond != NULL);
162 /* Be very careful: this is called for invalid conditions too */
163 if (cond->overlay)
164 gnm_style_unref (cond->overlay);
165 for (ui = 0; ui < 2; ui++)
166 gnm_style_cond_set_expr (cond, NULL, ui);
168 g_free (cond);
171 GType
172 gnm_style_cond_get_type (void)
174 static GType t = 0;
176 if (t == 0) {
177 t = g_boxed_type_register_static ("GnmStyleCond",
178 (GBoxedCopyFunc)gnm_style_cond_dup,
179 (GBoxedFreeFunc)gnm_style_cond_free);
181 return t;
185 * gnm_style_cond_get_sheet:
186 * @cond: #GnmStyleCond
188 * Returns: (transfer none): the #Sheet.
190 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;
197 void
198 gnm_style_cond_set_sheet (GnmStyleCond *cond, Sheet *sheet)
200 int ui;
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
212 * @idx: index
214 * Returns: (transfer none): the #GnmExprTop for the @idx'th condition.
216 GnmExprTop const *
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;
225 void
226 gnm_style_cond_set_expr (GnmStyleCond *cond,
227 GnmExprTop const *texpr,
228 unsigned idx)
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);
236 void
237 gnm_style_cond_set_overlay (GnmStyleCond *cond, GnmStyle *overlay)
239 g_return_if_fail (cond != NULL);
241 if (overlay)
242 gnm_style_ref (overlay);
243 if (cond->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));
258 } else if (force) {
260 * This is imperfect because the expression gets
261 * evaluated twice.
263 len_expr = gnm_expr_new_funcall1
264 (gnm_func_lookup_or_add_placeholder ("LEN"),
265 gnm_expr_copy (sexpr->expr));
266 } else
267 return NULL;
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),
273 len_expr),
274 negate ? GNM_EXPR_OP_NOT_EQUAL : GNM_EXPR_OP_EQUAL,
275 gnm_expr_copy (sexpr->expr));
280 * gnm_style_cond_get_alternate_expr:
281 * @cond: condition
283 * Returns: (transfer full) (allow-none): An custom expression that can be
284 * used in place of @cond.
286 GnmExprTop const *
287 gnm_style_cond_get_alternate_expr (GnmStyleCond const *cond)
289 GnmCellRef self;
290 GnmExpr const *expr;
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);
300 if (!sexpr)
301 return NULL;
304 switch (cond->op) {
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));
311 break;
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)));
322 break;
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)));
337 negate = FALSE;
338 break;
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);
349 negate = FALSE;
350 break;
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);
361 negate = FALSE;
362 break;
364 default:
365 return NULL;
368 if (negate)
369 expr = gnm_expr_new_funcall1
370 (gnm_func_lookup_or_add_placeholder ("NOT"), expr);
372 return gnm_expr_top_new (expr);
375 static gboolean
376 isself (GnmExpr const *expr)
378 GnmCellRef const *cr = gnm_expr_get_cellref (expr);
380 return (cr &&
381 cr->sheet == NULL &&
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));
419 return NULL;
423 * gnm_style_cond_canonicalize:
424 * @cond: condition
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.
430 void
431 gnm_style_cond_canonicalize (GnmStyleCond *cond)
433 GnmExpr const *expr, *expr2;
434 GnmExprTop const *texpr;
435 GnmValue const *v;
436 gboolean negate = FALSE;
437 gboolean match_negated;
438 GnmFunc const *iserror;
439 GnmFunc const *iferror;
440 GnmFunc const *find;
441 GnmStyleCondOp newop = GNM_STYLE_COND_CUSTOM;
443 g_return_if_fail (cond != NULL);
445 if (cond->op != GNM_STYLE_COND_CUSTOM)
446 return;
448 texpr = gnm_style_cond_get_expr (cond, 0);
449 if (!texpr)
450 return;
451 expr = texpr->expr;
452 texpr = NULL;
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")) {
457 negate = TRUE;
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])) {
468 newop = negate
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]));
478 newop = negate
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)
495 negate = !negate;
497 newop = negate
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]));
513 newop = negate
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);
528 if (texpr)
529 gnm_expr_top_unref (texpr);
530 cond->op = newop;
534 static gboolean
535 case_insensitive_has_fix (GnmValue const *vs, GnmValue const *vp,
536 gboolean is_prefix)
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);
541 GnmValue *vs2;
542 gboolean res;
544 if (plen > slen)
545 return FALSE;
547 vs2 = value_new_string_nocopy
548 (is_prefix
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);
552 value_release (vs2);
554 return res;
558 static gboolean
559 gnm_style_cond_eval (GnmStyleCond const *cond, GnmValue const *cv,
560 GnmEvalPos const *ep)
562 gboolean negate = FALSE;
563 gboolean res;
564 GnmValue *val0 = NULL;
565 GnmValue *val1 = NULL;
567 switch (gnm_style_cond_op_operands (cond->op)) {
568 case 2:
569 val1 = gnm_expr_top_eval (cond->deps[1].texpr, ep,
570 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
571 /* Fall through */
572 case 1:
573 val0 = gnm_expr_top_eval (cond->deps[0].texpr, ep,
574 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
575 /* Fall through */
576 case 0:
577 break;
578 default:
579 g_assert_not_reached ();
582 switch (cond->op) {
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;
587 break;
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;
593 break;
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;
599 break;
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);
606 break;
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);
612 break;
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) : "";
618 while (*s) {
619 gunichar uc = g_utf8_get_char (s);
620 if (!g_unichar_isspace (uc))
621 break;
622 s = g_utf8_next_char (s);
624 res = (*s == 0);
625 break;
628 case GNM_STYLE_COND_NOT_CONTAINS_STR:
629 negate = TRUE; /* ...and fall through */
630 case GNM_STYLE_COND_CONTAINS_STR:
631 res = (cv &&
632 gnm_excel_search_impl (value_peek_string (val0),
633 value_peek_string (cv),
634 0) >= 0);
635 break;
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));
641 break;
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));
647 break;
649 case GNM_STYLE_COND_CUSTOM:
650 res = value_get_as_bool (val0, NULL);
651 break;
653 default:
654 g_assert_not_reached ();
657 value_release (val0);
658 value_release (val1);
660 return negate ? !res : res;
663 static gboolean
664 gnm_style_cond_equal (GnmStyleCond const *ca, GnmStyleCond const *cb,
665 gboolean relax_sheet)
667 unsigned oi, N;
669 if (ca->op != cb->op)
670 return FALSE;
672 if (!gnm_style_equal (ca->overlay, cb->overlay))
673 return FALSE;
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)
678 return FALSE;
679 if (!gnm_expr_top_equal (ca->deps[oi].texpr,
680 cb->deps[oi].texpr))
681 return FALSE;
684 return TRUE;
688 static void
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);
698 static void
699 gnm_style_conditions_init (GnmStyleConditions *sc)
701 sc->conditions = NULL;
704 static void
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,
712 G_TYPE_OBJECT)
715 * gnm_style_conditions_new:
716 * @sheet: #Sheet
718 * Convenience tool to create a #GnmStyleCondition. Straight g_object_new
719 * will work too.
721 * Returns: (transfer full): a #GnmStyleConditions
723 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);
730 res->sheet = sheet;
731 return res;
735 * gnm_style_conditions_dup:
736 * @sc: (nullable): the #GnmStyleConditions to duplicate.
738 * Returns: (transfer full) (nullable): the duplicated #GnmStyleConditions.
740 GnmStyleConditions *
741 gnm_style_conditions_dup (GnmStyleConditions const *sc)
743 GnmStyleConditions *dup;
744 GPtrArray const *ga;
745 if (sc == NULL)
746 return NULL;
748 dup = gnm_style_conditions_new (gnm_style_conditions_get_sheet (sc));
749 ga = gnm_style_conditions_details (sc);
750 if (ga != NULL) {
751 guint i;
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;
759 return dup;
762 #define MIX(H) do { \
763 H *= G_GUINT64_CONSTANT(123456789012345); \
764 H ^= (H >> 31); \
765 } while (0)
767 guint32
768 gnm_style_conditions_hash (GnmStyleConditions const *sc)
770 guint64 hash = 42;
771 GPtrArray const *ga;
772 unsigned ui;
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);
784 if (cond->overlay)
785 hash ^= gnm_style_hash_XL (cond->overlay);
786 MIX (hash);
787 hash ^= cond->op;
788 MIX (hash);
791 return hash;
794 #undef MIX
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.
804 gboolean
805 gnm_style_conditions_equal (GnmStyleConditions const *sca,
806 GnmStyleConditions const *scb,
807 gboolean relax_sheet)
809 GPtrArray const *ga, *gb;
810 unsigned ui;
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)
816 return FALSE;
818 ga = gnm_style_conditions_details (sca);
819 gb = gnm_style_conditions_details (scb);
820 if (!ga || !gb)
821 return ga == gb;
822 if (ga->len != gb->len)
823 return FALSE;
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))
829 return FALSE;
832 return TRUE;
837 * gnm_style_conditions_get_sheet:
838 * @sc: #GnmStyleConditions
840 * Returns: (transfer none): the #Sheet.
842 Sheet *
843 gnm_style_conditions_get_sheet (GnmStyleConditions const *sc)
845 g_return_val_if_fail (sc != NULL, NULL);
846 return sc->sheet;
849 void
850 gnm_style_conditions_set_sheet (GnmStyleConditions *sc, Sheet *sheet)
852 GPtrArray const *ga;
853 unsigned ui;
855 g_return_if_fail (sc != NULL);
856 g_return_if_fail (IS_SHEET (sheet));
858 sc->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.
873 GPtrArray const *
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
885 * @pos: position.
887 * Insert @cond before @pos (append if @pos < 0).
889 void
890 gnm_style_conditions_insert (GnmStyleConditions *sc,
891 GnmStyleCond const *cond_, int pos)
893 GnmStyleCond *cond;
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);
908 if (pos >= 0) {
909 int i;
911 for (i = sc->conditions->len - 1;
912 i > pos;
913 i--)
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;
920 void
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;
931 } else
932 g_ptr_array_remove_index (sc->conditions, pos);
937 * gnm_style_conditions_overlay:
938 * @sc: #GnmStyleConditions
939 * @base: #GnmStyle
941 * Returns: (element-type GnmStyle) (transfer full): an array of #GnmStyle.
943 GPtrArray *
944 gnm_style_conditions_overlay (GnmStyleConditions const *sc,
945 GnmStyle const *base)
947 GPtrArray *res;
948 unsigned i;
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);
966 return res;
970 * gnm_style_conditions_eval:
971 * @sc: #GnmStyleConditions
972 * @pos: #GnmEvalPos
974 * Returns: the condition to use or -1 if none match.
977 gnm_style_conditions_eval (GnmStyleConditions const *sc, GnmEvalPos const *ep)
979 unsigned i;
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 ()) {
990 GnmParsePos pp;
991 parse_pos_init_evalpos (&pp, ep);
993 g_printerr ("Evaluating conditions %p at %s with %d clauses\n",
995 parsepos_as_string (&pp),
996 conds->len);
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);
1003 if (use_this) {
1004 if (debug_style_conds ())
1005 g_printerr (" Using clause %d\n", i);
1006 return i;
1010 if (debug_style_conds ())
1011 g_printerr (" No matching clauses\n");
1013 return -1;