Introspection fixes
[gnumeric.git] / src / style-conditions.c
blobe492505f4d6f6bbc12df5e6ab1dff27910ebe313
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);
209 GnmExprTop const *
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;
218 void
219 gnm_style_cond_set_expr (GnmStyleCond *cond,
220 GnmExprTop const *texpr,
221 unsigned idx)
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);
229 void
230 gnm_style_cond_set_overlay (GnmStyleCond *cond, GnmStyle *overlay)
232 g_return_if_fail (cond != NULL);
234 if (overlay)
235 gnm_style_ref (overlay);
236 if (cond->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));
251 } else if (force) {
253 * This is imperfect because the expression gets
254 * evaluated twice.
256 len_expr = gnm_expr_new_funcall1
257 (gnm_func_lookup_or_add_placeholder ("LEN"),
258 gnm_expr_copy (sexpr->expr));
259 } else
260 return NULL;
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),
266 len_expr),
267 negate ? GNM_EXPR_OP_NOT_EQUAL : GNM_EXPR_OP_EQUAL,
268 gnm_expr_copy (sexpr->expr));
273 * gnm_style_cond_get_alternate_expr:
274 * @cond: condition
276 * Returns: (transfer full) (allow-none): An custom expression that can be
277 * used in place of @cond.
279 GnmExprTop const *
280 gnm_style_cond_get_alternate_expr (GnmStyleCond const *cond)
282 GnmCellRef self;
283 GnmExpr const *expr;
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);
293 if (!sexpr)
294 return NULL;
297 switch (cond->op) {
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));
304 break;
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)));
315 break;
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)));
330 negate = FALSE;
331 break;
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);
342 negate = FALSE;
343 break;
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);
354 negate = FALSE;
355 break;
357 default:
358 return NULL;
361 if (negate)
362 expr = gnm_expr_new_funcall1
363 (gnm_func_lookup_or_add_placeholder ("NOT"), expr);
365 return gnm_expr_top_new (expr);
368 static gboolean
369 isself (GnmExpr const *expr)
371 GnmCellRef const *cr = gnm_expr_get_cellref (expr);
373 return (cr &&
374 cr->sheet == NULL &&
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));
412 return NULL;
416 * gnm_style_cond_canonicalize:
417 * @cond: condition
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.
423 void
424 gnm_style_cond_canonicalize (GnmStyleCond *cond)
426 GnmExpr const *expr, *expr2;
427 GnmExprTop const *texpr;
428 GnmValue const *v;
429 gboolean negate = FALSE;
430 gboolean match_negated;
431 GnmFunc const *iserror;
432 GnmFunc const *iferror;
433 GnmFunc const *find;
434 GnmStyleCondOp newop = GNM_STYLE_COND_CUSTOM;
436 g_return_if_fail (cond != NULL);
438 if (cond->op != GNM_STYLE_COND_CUSTOM)
439 return;
441 texpr = gnm_style_cond_get_expr (cond, 0);
442 if (!texpr)
443 return;
444 expr = texpr->expr;
445 texpr = NULL;
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")) {
450 negate = TRUE;
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])) {
461 newop = negate
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]));
471 newop = negate
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)
488 negate = !negate;
490 newop = negate
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]));
506 newop = negate
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);
521 if (texpr)
522 gnm_expr_top_unref (texpr);
523 cond->op = newop;
527 static gboolean
528 case_insensitive_has_fix (GnmValue const *vs, GnmValue const *vp,
529 gboolean is_prefix)
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);
534 GnmValue *vs2;
535 gboolean res;
537 if (plen > slen)
538 return FALSE;
540 vs2 = value_new_string_nocopy
541 (is_prefix
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);
545 value_release (vs2);
547 return res;
551 static gboolean
552 gnm_style_cond_eval (GnmStyleCond const *cond, GnmValue const *cv,
553 GnmEvalPos const *ep)
555 gboolean negate = FALSE;
556 gboolean res;
557 GnmValue *val0 = NULL;
558 GnmValue *val1 = NULL;
560 switch (gnm_style_cond_op_operands (cond->op)) {
561 case 2:
562 val1 = gnm_expr_top_eval (cond->deps[1].texpr, ep,
563 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
564 /* Fall through */
565 case 1:
566 val0 = gnm_expr_top_eval (cond->deps[0].texpr, ep,
567 GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
568 /* Fall through */
569 case 0:
570 break;
571 default:
572 g_assert_not_reached ();
575 switch (cond->op) {
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;
580 break;
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;
586 break;
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;
592 break;
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);
599 break;
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);
605 break;
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) : "";
611 while (*s) {
612 gunichar uc = g_utf8_get_char (s);
613 if (!g_unichar_isspace (uc))
614 break;
615 s = g_utf8_next_char (s);
617 res = (*s == 0);
618 break;
621 case GNM_STYLE_COND_NOT_CONTAINS_STR:
622 negate = TRUE; /* ...and fall through */
623 case GNM_STYLE_COND_CONTAINS_STR:
624 res = (cv &&
625 gnm_excel_search_impl (value_peek_string (val0),
626 value_peek_string (cv),
627 0) >= 0);
628 break;
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));
634 break;
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));
640 break;
642 case GNM_STYLE_COND_CUSTOM:
643 res = value_get_as_bool (val0, NULL);
644 break;
646 default:
647 g_assert_not_reached ();
650 value_release (val0);
651 value_release (val1);
653 return negate ? !res : res;
656 static gboolean
657 gnm_style_cond_equal (GnmStyleCond const *ca, GnmStyleCond const *cb,
658 gboolean relax_sheet)
660 unsigned oi, N;
662 if (ca->op != cb->op)
663 return FALSE;
665 if (!gnm_style_equal (ca->overlay, cb->overlay))
666 return FALSE;
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)
671 return FALSE;
672 if (!gnm_expr_top_equal (ca->deps[oi].texpr,
673 cb->deps[oi].texpr))
674 return FALSE;
677 return TRUE;
681 static void
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);
691 static void
692 gnm_style_conditions_init (GnmStyleConditions *sc)
694 sc->conditions = NULL;
697 static void
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,
705 G_TYPE_OBJECT)
708 * gnm_style_conditions_new:
710 * Convenience tool to create a GnmStyleCondition. Straight g_object_new
711 * will work too.
713 * Returns a GnmStyleConditions that the caller is responsible for.
715 GnmStyleConditions *
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);
722 res->sheet = sheet;
723 return res;
727 * gnm_style_conditions_dup:
728 * @sc: the #GnmStyleConditions to duplicate.
730 * Returns: (transfer full): the duplicated #GnmStyleConditions.
732 GnmStyleConditions *
733 gnm_style_conditions_dup (GnmStyleConditions const *sc)
735 GnmStyleConditions *dup;
736 GPtrArray const *ga;
737 if (sc == NULL)
738 return NULL;
740 dup = gnm_style_conditions_new (gnm_style_conditions_get_sheet (sc));
741 ga = gnm_style_conditions_details (sc);
742 if (ga != NULL) {
743 guint i;
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;
751 return dup;
754 #define MIX(H) do { \
755 H *= G_GUINT64_CONSTANT(123456789012345); \
756 H ^= (H >> 31); \
757 } while (0)
759 guint32
760 gnm_style_conditions_hash (GnmStyleConditions const *sc)
762 guint64 hash = 42;
763 GPtrArray const *ga;
764 unsigned ui;
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);
776 if (cond->overlay)
777 hash ^= gnm_style_hash_XL (cond->overlay);
778 MIX (hash);
779 hash ^= cond->op;
780 MIX (hash);
783 return hash;
786 #undef MIX
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.
796 gboolean
797 gnm_style_conditions_equal (GnmStyleConditions const *sca,
798 GnmStyleConditions const *scb,
799 gboolean relax_sheet)
801 GPtrArray const *ga, *gb;
802 unsigned ui;
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)
808 return FALSE;
810 ga = gnm_style_conditions_details (sca);
811 gb = gnm_style_conditions_details (scb);
812 if (!ga || !gb)
813 return ga == gb;
814 if (ga->len != gb->len)
815 return FALSE;
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))
821 return FALSE;
824 return TRUE;
829 * gnm_style_conditions_get_sheet:
830 * @sc: #GnmStyleConditions
832 * Returns: (transfer none): the #Sheet.
834 Sheet *
835 gnm_style_conditions_get_sheet (GnmStyleConditions const *sc)
837 g_return_val_if_fail (sc != NULL, NULL);
838 return sc->sheet;
841 void
842 gnm_style_conditions_set_sheet (GnmStyleConditions *sc, Sheet *sheet)
844 GPtrArray const *ga;
845 unsigned ui;
847 g_return_if_fail (sc != NULL);
848 g_return_if_fail (IS_SHEET (sheet));
850 sc->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.
865 GPtrArray const *
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
877 * @pos: position.
879 * Insert @cond before @pos (append if @pos < 0).
881 void
882 gnm_style_conditions_insert (GnmStyleConditions *sc,
883 GnmStyleCond const *cond_, int pos)
885 GnmStyleCond *cond;
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);
900 if (pos >= 0) {
901 int i;
903 for (i = sc->conditions->len - 1;
904 i > pos;
905 i--)
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;
912 void
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;
923 } else
924 g_ptr_array_remove_index (sc->conditions, pos);
929 * gnm_style_conditions_overlay:
930 * @sc: #GnmStyleConditions
931 * @base: #GnmStyle
933 * Returns: (element-type GnmStyle) (transfer full): an array of #GnmStyle.
935 GPtrArray *
936 gnm_style_conditions_overlay (GnmStyleConditions const *sc,
937 GnmStyle const *base)
939 GPtrArray *res;
940 unsigned i;
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);
958 return res;
962 * gnm_style_conditions_eval:
963 * @sc: #GnmStyleConditions
964 * @pos: #GnmEvalPos
966 * Returns: the condition to use or -1 if none match.
969 gnm_style_conditions_eval (GnmStyleConditions const *sc, GnmEvalPos const *ep)
971 unsigned i;
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 ()) {
982 GnmParsePos pp;
983 parse_pos_init_evalpos (&pp, ep);
985 g_printerr ("Evaluating conditions %p at %s with %d clauses\n",
987 parsepos_as_string (&pp),
988 conds->len);
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);
995 if (use_this) {
996 if (debug_style_conds ())
997 g_printerr (" Using clause %d\n", i);
998 return i;
1002 if (debug_style_conds ())
1003 g_printerr (" No matching clauses\n");
1005 return -1;