Introspection fixes
[gnumeric.git] / src / sheet-filter.c
blobf88d93eed33bd672e84c91b4c7e45b846d41e75b
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * sheet-filter.c: support for 'auto-filters'
6 * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 * USA
23 #include <gnumeric-config.h>
24 #include "libgnumeric.h"
25 #include "sheet-filter.h"
26 #include "sheet-filter-combo.h"
28 #include "workbook.h"
29 #include "sheet.h"
30 #include "sheet-private.h"
31 #include "cell.h"
32 #include "expr.h"
33 #include "value.h"
34 #include "gnm-format.h"
35 #include "ranges.h"
36 #include "number-match.h"
37 #include "gutils.h"
38 #include "sheet-object.h"
39 #include "gnm-filter-combo-view.h"
40 #include "gnm-cell-combo-view.h"
41 #include <gsf/gsf-impl-utils.h>
43 #include <glib/gi18n-lib.h>
44 #include <stdlib.h>
45 #include <string.h>
47 static gboolean
48 gnm_filter_op_needs_value (GnmFilterOp op)
50 g_return_val_if_fail (op != GNM_FILTER_UNUSED, FALSE);
52 switch (op & GNM_FILTER_OP_TYPE_MASK) {
53 case GNM_FILTER_OP_TYPE_OP:
54 case GNM_FILTER_OP_TYPE_BUCKETS:
55 case GNM_FILTER_OP_TYPE_MATCH:
56 return TRUE;
57 default:
58 g_assert_not_reached ();
59 case GNM_FILTER_OP_TYPE_BLANKS:
60 case GNM_FILTER_OP_TYPE_AVERAGE:
61 case GNM_FILTER_OP_TYPE_STDDEV:
62 return FALSE;
67 /**
68 * gnm_filter_condition_new_single :
69 * @op: #GnmFilterOp
70 * @v: #GnmValue
72 * Create a new condition with 1 value.
73 * Absorbs the reference to @v.
74 **/
75 GnmFilterCondition *
76 gnm_filter_condition_new_single (GnmFilterOp op, GnmValue *v)
78 GnmFilterCondition *res;
80 g_return_val_if_fail ((v != NULL) == gnm_filter_op_needs_value (op),
81 (value_release (v), NULL));
83 res = g_new0 (GnmFilterCondition, 1);
84 res->op[0] = op; res->op[1] = GNM_FILTER_UNUSED;
85 res->value[0] = v;
86 return res;
89 /**
90 * gnm_filter_condition_new_double :
91 * @op0: #GnmFilterOp
92 * @v0: #GnmValue
93 * @join_with_and:
94 * @op1: #GnmFilterOp
95 * @v1: #GnmValue
97 * Create a new condition with 2 value.
98 * Absorbs the reference to @v0 and @v1.
99 **/
100 GnmFilterCondition *
101 gnm_filter_condition_new_double (GnmFilterOp op0, GnmValue *v0,
102 gboolean join_with_and,
103 GnmFilterOp op1, GnmValue *v1)
105 GnmFilterCondition *res;
107 g_return_val_if_fail ((v0 != NULL) == gnm_filter_op_needs_value (op0),
108 (value_release (v0), value_release (v1), NULL));
109 g_return_val_if_fail ((v1 != NULL) == gnm_filter_op_needs_value (op1),
110 (value_release (v0), value_release (v1), NULL));
112 res = g_new0 (GnmFilterCondition, 1);
113 res->op[0] = op0; res->op[1] = op1;
114 res->is_and = join_with_and;
115 res->value[0] = v0; res->value[1] = v1;
116 return res;
119 GnmFilterCondition *
120 gnm_filter_condition_new_bucket (gboolean top, gboolean absolute,
121 gboolean rel_range, double n)
123 GnmFilterCondition *res = g_new0 (GnmFilterCondition, 1);
124 res->op[0] = GNM_FILTER_OP_TOP_N | (top ? 0 : 1) |
125 (absolute ? 0 : (rel_range ? 2 : 4));
126 res->op[1] = GNM_FILTER_UNUSED;
127 res->count = n;
128 return res;
131 GnmFilterCondition *
132 gnm_filter_condition_dup (GnmFilterCondition const *src)
134 GnmFilterCondition *dst;
136 if (src == NULL)
137 return NULL;
139 dst = g_new0 (GnmFilterCondition, 1);
140 dst->op[0] = src->op[0];
141 dst->op[1] = src->op[1];
142 dst->is_and = src->is_and;
143 dst->count = src->count;
144 dst->value[0] = value_dup (src->value[0]);
145 dst->value[1] = value_dup (src->value[1]);
146 return dst;
149 void
150 gnm_filter_condition_free (GnmFilterCondition *cond)
152 if (cond == NULL)
153 return;
155 value_release (cond->value[0]);
156 value_release (cond->value[1]);
157 g_free (cond);
160 GType
161 gnm_filter_condition_get_type (void)
163 static GType t = 0;
165 if (t == 0) {
166 t = g_boxed_type_register_static ("GnmFilterCondition",
167 (GBoxedCopyFunc)gnm_filter_condition_dup,
168 (GBoxedFreeFunc)gnm_filter_condition_free);
170 return t;
173 /*****************************************************************************/
175 typedef struct {
176 GnmFilterCondition const *cond;
177 GnmValue *val[2];
178 GnmValue *alt_val[2];
179 GORegexp regexp[2];
180 Sheet *target_sheet; /* not necessarilly the src */
181 } FilterExpr;
183 static void
184 filter_expr_init (FilterExpr *fexpr, unsigned i,
185 GnmFilterCondition const *cond,
186 GnmFilter const *filter)
188 GnmValue *tmp = cond->value[i];
190 if (tmp && VALUE_IS_STRING (tmp)) {
191 GnmFilterOp op = cond->op[i];
192 char const *str = value_peek_string (tmp);
193 GODateConventions const *date_conv =
194 workbook_date_conv (filter->sheet->workbook);
196 if ((op == GNM_FILTER_OP_EQUAL || op == GNM_FILTER_OP_NOT_EQUAL) &&
197 gnm_regcomp_XL (fexpr->regexp + i, str, GO_REG_ICASE, TRUE, TRUE) == GO_REG_OK) {
198 /* FIXME: Do we want to anchor at the end above? */
199 fexpr->val[i] = NULL;
200 return;
203 fexpr->val[i] = format_match_number (str, NULL, date_conv);
204 if (fexpr->val[i] != NULL)
205 return;
207 fexpr->val[i] = value_dup (tmp);
210 static void
211 filter_expr_release (FilterExpr *fexpr, unsigned i)
213 if (fexpr->val[i] == NULL)
214 go_regfree (fexpr->regexp + i);
215 else
216 value_release (fexpr->val[i]);
219 static char *
220 filter_cell_contents (GnmCell *cell)
222 GOFormat const *format = gnm_cell_get_format (cell);
223 GODateConventions const *date_conv =
224 workbook_date_conv (cell->base.sheet->workbook);
225 return format_value (format, cell->value, -1, date_conv);
228 static gboolean
229 filter_expr_eval (GnmFilterOp op, GnmValue const *src, GORegexp const *regexp,
230 GnmCell *cell)
232 GnmValue *target = cell->value;
233 GnmValDiff cmp;
234 GnmValue *fake_val = NULL;
236 if (src == NULL) {
237 char *str = filter_cell_contents (cell);
238 GORegmatch rm;
239 int res = go_regexec (regexp, str, 1, &rm, 0);
240 gboolean whole = (res == GO_REG_OK && rm.rm_so == 0 && str[rm.rm_eo] == 0);
242 g_free (str);
244 switch (res) {
245 case GO_REG_OK:
246 if (whole)
247 return op == GNM_FILTER_OP_EQUAL;
248 /* fall through */
250 case GO_REG_NOMATCH:
251 return op == GNM_FILTER_OP_NOT_EQUAL;
253 default:
254 g_warning ("Unexpected regexec result");
255 return FALSE;
259 if (VALUE_IS_STRING (target) && VALUE_IS_NUMBER (src)) {
260 GODateConventions const *date_conv =
261 workbook_date_conv (cell->base.sheet->workbook);
262 char *str = format_value (NULL, src, -1, date_conv);
263 fake_val = value_new_string_nocopy (str);
264 src = fake_val;
267 cmp = value_compare (target, src, FALSE);
268 value_release (fake_val);
270 switch (op) {
271 case GNM_FILTER_OP_EQUAL : return cmp == IS_EQUAL;
272 case GNM_FILTER_OP_NOT_EQUAL : return cmp != IS_EQUAL;
273 case GNM_FILTER_OP_GTE : if (cmp == IS_EQUAL) return TRUE; /* fall */
274 case GNM_FILTER_OP_GT : return cmp == IS_GREATER;
275 case GNM_FILTER_OP_LTE : if (cmp == IS_EQUAL) return TRUE; /* fall */
276 case GNM_FILTER_OP_LT : return cmp == IS_LESS;
277 default :
278 g_warning ("Huh?");
279 return FALSE;
283 static GnmValue *
284 cb_filter_expr (GnmCellIter const *iter, FilterExpr const *fexpr)
286 if (iter->cell != NULL) {
287 unsigned int ui;
289 for (ui = 0; ui < G_N_ELEMENTS (fexpr->cond->op); ui++) {
290 gboolean res;
292 if (fexpr->cond->op[ui] == GNM_FILTER_UNUSED)
293 continue;
295 res = filter_expr_eval (fexpr->cond->op[ui],
296 fexpr->val[ui],
297 fexpr->regexp + ui,
298 iter->cell);
299 if (fexpr->cond->is_and && !res)
300 goto nope; /* AND(...,FALSE,...) */
301 if (res && !fexpr->cond->is_and)
302 return NULL; /* OR(...,TRUE,...) */
305 if (fexpr->cond->is_and)
306 return NULL; /* AND(TRUE,...,TRUE) */
309 nope:
310 colrow_set_visibility (fexpr->target_sheet, FALSE, FALSE,
311 iter->pp.eval.row, iter->pp.eval.row);
312 return NULL;
315 /*****************************************************************************/
317 static GnmValue *
318 cb_filter_non_blanks (GnmCellIter const *iter, Sheet *target_sheet)
320 if (gnm_cell_is_blank (iter->cell))
321 colrow_set_visibility (target_sheet, FALSE, FALSE,
322 iter->pp.eval.row, iter->pp.eval.row);
323 return NULL;
326 static GnmValue *
327 cb_filter_blanks (GnmCellIter const *iter, Sheet *target_sheet)
329 if (!gnm_cell_is_blank (iter->cell))
330 colrow_set_visibility (target_sheet, FALSE, FALSE,
331 iter->pp.eval.row, iter->pp.eval.row);
332 return NULL;
335 /*****************************************************************************/
337 typedef struct {
338 unsigned count;
339 unsigned elements;
340 gboolean find_max;
341 GnmValue const **vals;
342 Sheet *target_sheet;
343 } FilterItems;
345 static GnmValue *
346 cb_filter_find_items (GnmCellIter const *iter, FilterItems *data)
348 GnmValue const *v = iter->cell->value;
349 if (data->elements >= data->count) {
350 unsigned j, i = data->elements;
351 GnmValDiff const cond = data->find_max ? IS_GREATER : IS_LESS;
352 while (i-- > 0)
353 if (value_compare (v, data->vals[i], TRUE) == cond) {
354 for (j = 0; j < i ; j++)
355 data->vals[j] = data->vals[j+1];
356 data->vals[i] = v;
357 break;
359 } else {
360 data->vals [data->elements++] = v;
361 if (data->elements == data->count) {
362 qsort (data->vals, data->elements,
363 sizeof (GnmValue *),
364 data->find_max ? value_cmp : value_cmp_reverse);
367 return NULL;
370 static GnmValue *
371 cb_hide_unwanted_items (GnmCellIter const *iter, FilterItems const *data)
373 if (iter->cell != NULL) {
374 int i = data->elements;
375 GnmValue const *v = iter->cell->value;
377 while (i-- > 0)
378 if (data->vals[i] == v)
379 return NULL;
381 colrow_set_visibility (data->target_sheet, FALSE, FALSE,
382 iter->pp.eval.row, iter->pp.eval.row);
383 return NULL;
386 /*****************************************************************************/
388 typedef struct {
389 gboolean initialized, find_max;
390 gnm_float low, high;
391 Sheet *target_sheet;
392 } FilterPercentage;
394 static GnmValue *
395 cb_filter_find_percentage (GnmCellIter const *iter, FilterPercentage *data)
397 if (VALUE_IS_NUMBER (iter->cell->value)) {
398 gnm_float const v = value_get_as_float (iter->cell->value);
400 if (data->initialized) {
401 if (data->low > v)
402 data->low = v;
403 else if (data->high < v)
404 data->high = v;
405 } else {
406 data->initialized = TRUE;
407 data->low = data->high = v;
410 return NULL;
413 static GnmValue *
414 cb_hide_unwanted_percentage (GnmCellIter const *iter,
415 FilterPercentage const *data)
417 if (iter->cell != NULL && VALUE_IS_NUMBER (iter->cell->value)) {
418 gnm_float const v = value_get_as_float (iter->cell->value);
419 if (data->find_max) {
420 if (v >= data->high)
421 return NULL;
422 } else {
423 if (v <= data->low)
424 return NULL;
427 colrow_set_visibility (data->target_sheet, FALSE, FALSE,
428 iter->pp.eval.row, iter->pp.eval.row);
429 return NULL;
431 /*****************************************************************************/
434 gnm_filter_combo_index (GnmFilterCombo *fcombo)
436 g_return_val_if_fail (GNM_IS_FILTER_COMBO (fcombo), 0);
438 return (sheet_object_get_range (GNM_SO (fcombo))->start.col -
439 fcombo->filter->r.start.col);
444 * gnm_filter_combo_apply :
445 * @fcombo: #GnmFilterCombo
446 * @target_sheet: @Sheet
449 void
450 gnm_filter_combo_apply (GnmFilterCombo *fcombo, Sheet *target_sheet)
452 GnmFilter const *filter;
453 GnmFilterCondition const *cond;
454 int col, start_row, end_row;
455 CellIterFlags iter_flags = CELL_ITER_IGNORE_HIDDEN;
457 g_return_if_fail (GNM_IS_FILTER_COMBO (fcombo));
459 filter = fcombo->filter;
460 cond = fcombo->cond;
461 col = sheet_object_get_range (GNM_SO (fcombo))->start.col;
462 start_row = filter->r.start.row + 1;
463 end_row = filter->r.end.row;
465 if (start_row > end_row ||
466 cond == NULL ||
467 cond->op[0] == GNM_FILTER_UNUSED)
468 return;
471 * For the combo we filter a temporary sheet using the data from
472 * filter->sheet and need to include everything from the source,
473 * because it has a different set of conditions
475 if (target_sheet != filter->sheet)
476 iter_flags = CELL_ITER_ALL;
478 if (0x10 >= (cond->op[0] & GNM_FILTER_OP_TYPE_MASK)) {
479 FilterExpr data;
480 data.cond = cond;
481 data.target_sheet = target_sheet;
482 filter_expr_init (&data, 0, cond, filter);
483 if (cond->op[1] != GNM_FILTER_UNUSED)
484 filter_expr_init (&data, 1, cond, filter);
486 sheet_foreach_cell_in_range (filter->sheet,
487 iter_flags,
488 col, start_row, col, end_row,
489 (CellIterFunc) cb_filter_expr, &data);
491 filter_expr_release (&data, 0);
492 if (cond->op[1] != GNM_FILTER_UNUSED)
493 filter_expr_release (&data, 1);
494 } else if (cond->op[0] == GNM_FILTER_OP_BLANKS)
495 sheet_foreach_cell_in_range (filter->sheet,
496 CELL_ITER_IGNORE_HIDDEN,
497 col, start_row, col, end_row,
498 (CellIterFunc) cb_filter_blanks, target_sheet);
499 else if (cond->op[0] == GNM_FILTER_OP_NON_BLANKS)
500 sheet_foreach_cell_in_range (filter->sheet,
501 CELL_ITER_IGNORE_HIDDEN,
502 col, start_row, col, end_row,
503 (CellIterFunc) cb_filter_non_blanks, target_sheet);
504 else if (0x30 == (cond->op[0] & GNM_FILTER_OP_TYPE_MASK)) {
505 if (cond->op[0] & GNM_FILTER_OP_PERCENT_MASK) { /* relative */
506 if (cond->op[0] & GNM_FILTER_OP_REL_N_MASK) {
507 FilterItems data;
508 data.find_max = (cond->op[0] & 0x1) ? FALSE : TRUE;
509 data.elements = 0;
510 data.count = 0.5 + cond->count * (end_row - start_row + 1) /100.;
511 if (data.count < 1)
512 data.count = 1;
513 data.vals = g_new (GnmValue const *, data.count);
514 sheet_foreach_cell_in_range (filter->sheet,
515 CELL_ITER_IGNORE_HIDDEN | CELL_ITER_IGNORE_BLANK,
516 col, start_row, col, end_row,
517 (CellIterFunc) cb_filter_find_items, &data);
518 data.target_sheet = target_sheet;
519 sheet_foreach_cell_in_range (filter->sheet,
520 CELL_ITER_IGNORE_HIDDEN,
521 col, start_row, col, end_row,
522 (CellIterFunc) cb_hide_unwanted_items, &data);
523 g_free (data.vals);
524 } else {
525 FilterPercentage data;
526 gnm_float offset;
528 data.find_max = (cond->op[0] & 0x1) ? FALSE : TRUE;
529 data.initialized = FALSE;
530 sheet_foreach_cell_in_range (filter->sheet,
531 CELL_ITER_IGNORE_HIDDEN | CELL_ITER_IGNORE_BLANK,
532 col, start_row, col, end_row,
533 (CellIterFunc) cb_filter_find_percentage, &data);
534 offset = (data.high - data.low) * cond->count / 100.;
535 data.high -= offset;
536 data.low += offset;
537 data.target_sheet = target_sheet;
538 sheet_foreach_cell_in_range (filter->sheet,
539 CELL_ITER_IGNORE_HIDDEN,
540 col, start_row, col, end_row,
541 (CellIterFunc) cb_hide_unwanted_percentage, &data);
543 } else { /* absolute */
544 FilterItems data;
545 data.find_max = (cond->op[0] & 0x1) ? FALSE : TRUE;
546 data.elements = 0;
547 data.count = cond->count;
548 data.vals = g_new (GnmValue const *, data.count);
550 sheet_foreach_cell_in_range (filter->sheet,
551 CELL_ITER_IGNORE_HIDDEN | CELL_ITER_IGNORE_BLANK,
552 col, start_row, col, end_row,
553 (CellIterFunc) cb_filter_find_items, &data);
554 data.target_sheet = target_sheet;
555 sheet_foreach_cell_in_range (filter->sheet,
556 CELL_ITER_IGNORE_HIDDEN,
557 col, start_row, col, end_row,
558 (CellIterFunc) cb_hide_unwanted_items, &data);
559 g_free (data.vals);
561 } else
562 g_warning ("Invalid operator %d", cond->op[0]);
565 enum {
566 COND_CHANGED,
567 LAST_SIGNAL
570 static guint signals [LAST_SIGNAL] = { 0 };
572 typedef struct {
573 SheetObjectClass parent;
575 void (*cond_changed) (GnmFilterCombo *);
576 } GnmFilterComboClass;
578 static void
579 gnm_filter_combo_finalize (GObject *object)
581 GnmFilterCombo *fcombo = GNM_FILTER_COMBO (object);
582 GObjectClass *parent;
584 gnm_filter_condition_free (fcombo->cond);
585 fcombo->cond = NULL;
587 parent = g_type_class_peek (GNM_SO_TYPE);
588 parent->finalize (object);
591 static void
592 gnm_filter_combo_init (SheetObject *so)
594 /* keep the arrows from wandering with their cells */
595 so->flags &= ~SHEET_OBJECT_MOVE_WITH_CELLS;
597 static SheetObjectView *
598 gnm_filter_combo_view_new (SheetObject *so, SheetObjectViewContainer *container)
600 return gnm_cell_combo_view_new (so,
601 gnm_filter_combo_view_get_type (), container);
603 static void
604 gnm_filter_combo_class_init (GObjectClass *gobject_class)
606 SheetObjectClass *so_class = GNM_SO_CLASS (gobject_class);
608 /* Object class method overrides */
609 gobject_class->finalize = gnm_filter_combo_finalize;
611 /* SheetObject class method overrides */
612 so_class->new_view = gnm_filter_combo_view_new;
613 so_class->write_xml_sax = NULL;
614 so_class->prep_sax_parser = NULL;
615 so_class->copy = NULL;
617 signals[COND_CHANGED] = g_signal_new ("cond-changed",
618 GNM_FILTER_COMBO_TYPE,
619 G_SIGNAL_RUN_LAST,
620 G_STRUCT_OFFSET (GnmFilterComboClass, cond_changed),
621 NULL, NULL,
622 g_cclosure_marshal_VOID__VOID,
623 G_TYPE_NONE, 0);
626 GSF_CLASS (GnmFilterCombo, gnm_filter_combo,
627 gnm_filter_combo_class_init, gnm_filter_combo_init,
628 GNM_SO_TYPE)
630 /*************************************************************************/
632 static void
633 gnm_filter_add_field (GnmFilter *filter, int i)
635 /* pretend to fill the cell, then clip the X start later */
636 static double const a_offsets[4] = { .0, .0, 1., 1. };
637 int n;
638 GnmRange tmp;
639 SheetObjectAnchor anchor;
640 GnmFilterCombo *fcombo = g_object_new (GNM_FILTER_COMBO_TYPE, NULL);
642 fcombo->filter = filter;
643 tmp.start.row = tmp.end.row = filter->r.start.row;
644 tmp.start.col = tmp.end.col = filter->r.start.col + i;
645 sheet_object_anchor_init (&anchor, &tmp, a_offsets,
646 GOD_ANCHOR_DIR_DOWN_RIGHT, GNM_SO_ANCHOR_TWO_CELLS);
647 sheet_object_set_anchor (GNM_SO (fcombo), &anchor);
648 sheet_object_set_sheet (GNM_SO (fcombo), filter->sheet);
650 g_ptr_array_add (filter->fields, NULL);
651 for (n = filter->fields->len; --n > i ; )
652 g_ptr_array_index (filter->fields, n) =
653 g_ptr_array_index (filter->fields, n - 1);
654 g_ptr_array_index (filter->fields, n) = fcombo;
655 /* We hold a reference to fcombo */
658 void
659 gnm_filter_attach (GnmFilter *filter, Sheet *sheet)
661 int i;
663 g_return_if_fail (filter != NULL);
664 g_return_if_fail (filter->sheet == NULL);
665 g_return_if_fail (IS_SHEET (sheet));
667 gnm_filter_ref (filter);
669 filter->sheet = sheet;
670 sheet->filters = g_slist_prepend (sheet->filters, filter);
671 sheet->priv->filters_changed = TRUE;
673 for (i = 0 ; i < range_width (&(filter->r)); i++)
674 gnm_filter_add_field (filter, i);
679 * gnm_filter_new :
680 * @sheet:
681 * @r:
683 * Init a filter and add it to @sheet
685 GnmFilter *
686 gnm_filter_new (Sheet *sheet, GnmRange const *r)
688 GnmFilter *filter;
690 g_return_val_if_fail (IS_SHEET (sheet), NULL);
691 g_return_val_if_fail (r != NULL, NULL);
693 filter = g_new0 (GnmFilter, 1);
695 filter->is_active = FALSE;
696 filter->r = *r;
697 filter->fields = g_ptr_array_new ();
699 /* This creates the initial ref. */
700 gnm_filter_attach (filter, sheet);
702 return filter;
706 * gnm_filter_dup :
707 * @src: #GnmFilter
708 * @sheet: #Sheet
710 * Duplicate @src into @sheet
712 GnmFilter *
713 gnm_filter_dup (GnmFilter const *src, Sheet *sheet)
715 int i;
716 GnmFilter *dst;
718 g_return_val_if_fail (src != NULL, NULL);
719 g_return_val_if_fail (IS_SHEET (sheet), NULL);
721 dst = g_new0 (GnmFilter, 1);
723 dst->is_active = src->is_active;
724 dst->r = src->r;
725 dst->fields = g_ptr_array_new ();
727 /* This creates the initial ref. */
728 gnm_filter_attach (dst, sheet);
730 for (i = 0 ; i < range_width (&dst->r); i++) {
731 gnm_filter_add_field (dst, i);
732 gnm_filter_set_condition (dst, i,
733 gnm_filter_condition_dup (
734 gnm_filter_get_condition (src, i)),
735 FALSE);
738 return dst;
741 GnmFilter *
742 gnm_filter_ref (GnmFilter *filter)
744 g_return_val_if_fail (filter != NULL, NULL);
745 filter->ref_count++;
746 return filter;
749 void
750 gnm_filter_unref (GnmFilter *filter)
752 g_return_if_fail (filter != NULL);
754 filter->ref_count--;
755 if (filter->ref_count > 0)
756 return;
758 g_ptr_array_free (filter->fields, TRUE);
759 g_free (filter);
762 GType
763 gnm_filter_get_type (void)
765 static GType t = 0;
767 if (t == 0) {
768 t = g_boxed_type_register_static ("GnmFilter",
769 (GBoxedCopyFunc)gnm_filter_ref,
770 (GBoxedFreeFunc)gnm_filter_unref);
772 return t;
775 void
776 gnm_filter_remove (GnmFilter *filter)
778 Sheet *sheet;
779 int i;
781 g_return_if_fail (filter != NULL);
782 g_return_if_fail (filter->sheet != NULL);
784 sheet = filter->sheet;
785 sheet->priv->filters_changed = TRUE;
786 sheet->filters = g_slist_remove (sheet->filters, filter);
787 for (i = filter->r.start.row; ++i <= filter->r.end.row ; ) {
788 ColRowInfo *ri = sheet_row_get (sheet, i);
789 if (ri != NULL) {
790 ri->in_filter = FALSE;
791 colrow_set_visibility (sheet, FALSE, TRUE, i, i);
794 filter->sheet = NULL;
796 for (i = 0 ; i < (int)filter->fields->len ; i++) {
797 SheetObject *so = g_ptr_array_index (filter->fields, i);
798 sheet_object_clear_sheet (so);
799 g_object_unref (so);
801 g_ptr_array_set_size (filter->fields, 0);
805 * gnm_filter_get_condition :
806 * @filter:
807 * @i:
810 GnmFilterCondition const *
811 gnm_filter_get_condition (GnmFilter const *filter, unsigned i)
813 GnmFilterCombo *fcombo;
815 g_return_val_if_fail (filter != NULL, NULL);
816 g_return_val_if_fail (i < filter->fields->len, NULL);
818 fcombo = g_ptr_array_index (filter->fields, i);
819 return fcombo->cond;
822 void
823 gnm_filter_reapply (GnmFilter *filter)
825 unsigned i;
827 colrow_set_visibility (filter->sheet, FALSE, TRUE,
828 filter->r.start.row + 1, filter->r.end.row);
829 for (i = 0 ; i < filter->fields->len ; i++)
830 gnm_filter_combo_apply (g_ptr_array_index (filter->fields, i),
831 filter->sheet);
834 static void
835 gnm_filter_update_active (GnmFilter *filter)
837 unsigned i;
838 gboolean old_active = filter->is_active;
840 filter->is_active = FALSE;
841 for (i = 0 ; i < filter->fields->len ; i++) {
842 GnmFilterCombo *fcombo = g_ptr_array_index (filter->fields, i);
843 if (fcombo->cond != NULL) {
844 filter->is_active = TRUE;
845 break;
849 if (filter->is_active != old_active) {
850 int r;
851 for (r = filter->r.start.row; ++r <= filter->r.end.row ; ) {
852 ColRowInfo *ri = sheet_row_fetch (filter->sheet, r);
853 ri->in_filter = filter->is_active;
860 * gnm_filter_set_condition :
861 * @filter:
862 * @i:
863 * @cond: #GnmFilterCondition
864 * @apply:
866 * Change the @i-th condition of @filter to @cond. If @apply is
867 * TRUE @filter is used to set the visibility of the rows in @filter::sheet
869 * Absorbs the reference to @cond.
871 void
872 gnm_filter_set_condition (GnmFilter *filter, unsigned i,
873 GnmFilterCondition *cond,
874 gboolean apply)
876 GnmFilterCombo *fcombo;
877 gboolean existing_cond = FALSE;
879 g_return_if_fail (filter != NULL);
880 g_return_if_fail (i < filter->fields->len);
882 fcombo = g_ptr_array_index (filter->fields, i);
884 if (fcombo->cond != NULL) {
885 existing_cond = TRUE;
886 gnm_filter_condition_free (fcombo->cond);
888 fcombo->cond = cond;
889 g_signal_emit (G_OBJECT (fcombo), signals [COND_CHANGED], 0);
891 if (apply) {
892 /* if there was an existing cond then we need to do
893 * redo the whole filter.
894 * This is because we do not record what elements this
895 * particular field filtered
897 if (existing_cond)
898 gnm_filter_reapply (filter);
899 else
900 /* When adding a new cond all we need to do is
901 * apply that filter */
902 gnm_filter_combo_apply (fcombo, filter->sheet);
905 gnm_filter_update_active (filter);
909 * gnm_filter_overlaps_range :
910 * @filter: #GnmFilter
911 * @r: #GnmRange
913 * Returns: %TRUE if @filter overlaps @r.
915 static gboolean
916 gnm_filter_overlaps_range (GnmFilter const *filter, GnmRange const *r)
918 g_return_val_if_fail (filter != NULL, FALSE);
919 g_return_val_if_fail (r != NULL, FALSE);
921 return range_overlap (&filter->r, r);
924 /*************************************************************************/
927 * gnm_sheet_filter_at_pos :
928 * @sheet: #Sheet
930 * Returns : #GnmRange
932 GnmFilter *
933 gnm_sheet_filter_at_pos (Sheet const *sheet, GnmCellPos const *pos)
935 GSList *ptr;
936 GnmRange r;
938 g_return_val_if_fail (IS_SHEET (sheet), NULL);
939 g_return_val_if_fail (NULL != pos, NULL);
941 range_init_cellpos (&r, pos);
942 for (ptr = sheet->filters; ptr != NULL ; ptr = ptr->next)
943 if (gnm_filter_overlaps_range (ptr->data, &r))
944 return ptr->data;
946 return NULL;
950 * gnm_sheet_filter_intersect_rows:
951 * @sheet:
952 * @from: starting row number
953 * @to: ending row number
955 * Returns: the filter, if any, that intersect the rows @from to @to
957 GnmFilter *
958 gnm_sheet_filter_intersect_rows (Sheet const *sheet, int from, int to)
960 GSList *ptr;
961 GnmRange r;
963 g_return_val_if_fail (IS_SHEET (sheet), NULL);
965 range_init_rows (&r, sheet, from, to);
966 for (ptr = sheet->filters; ptr != NULL ; ptr = ptr->next)
967 if (gnm_filter_overlaps_range (ptr->data, &r))
968 return ptr->data;
970 return NULL;
973 GnmRange *
974 gnm_sheet_filter_can_be_extended (G_GNUC_UNUSED Sheet const *sheet,
975 GnmFilter const *f, GnmRange const *r)
977 if (r->start.row < f->r.start.row || r->end.row > f->r.end.row)
978 return NULL;
979 if ((r->end.col > f->r.end.col) ||
980 (r->start.col < f->r.start.col)) {
981 GnmRange *res = g_new (GnmRange, 1);
982 *res = range_union (&f->r, r);
983 return res;
985 return NULL;
989 /*************************************************************************/
991 struct cb_remove_col_undo {
992 unsigned col;
993 GnmFilterCondition *cond;
996 static void
997 cb_remove_col_undo_free (struct cb_remove_col_undo *r)
999 gnm_filter_condition_free (r->cond);
1000 g_free (r);
1003 static void
1004 cb_remove_col_undo (GnmFilter *filter, struct cb_remove_col_undo *r,
1005 G_GNUC_UNUSED gpointer data)
1007 while (filter->fields->len <= r->col)
1008 gnm_filter_add_field (filter, filter->fields->len);
1009 gnm_filter_set_condition (filter, r->col,
1010 gnm_filter_condition_dup (r->cond),
1011 FALSE);
1014 static void
1015 remove_col (GnmFilter *filter, unsigned col, GOUndo **pundo)
1017 GnmFilterCombo *fcombo = g_ptr_array_index (filter->fields, col);
1018 if (pundo) {
1019 struct cb_remove_col_undo *r = g_new (struct cb_remove_col_undo, 1);
1020 GOUndo *u;
1022 r->col = col;
1023 r->cond = gnm_filter_condition_dup (fcombo->cond);
1024 u = go_undo_binary_new
1025 (gnm_filter_ref (filter), r,
1026 (GOUndoBinaryFunc)cb_remove_col_undo,
1027 (GFreeFunc)gnm_filter_unref,
1028 (GFreeFunc)cb_remove_col_undo_free);
1029 *pundo = go_undo_combine (*pundo, u);
1031 g_object_unref (fcombo);
1032 g_ptr_array_remove_index (filter->fields, col);
1035 static void
1036 gnm_filter_set_range (GnmFilter *filter, GnmRange *r)
1038 GnmRange old_r = filter->r;
1039 int i;
1040 int start = r->start.col;
1042 filter->r = *r;
1043 for (i = start; i < old_r.start.col; i++)
1044 gnm_filter_add_field (filter, i - start);
1045 for (i = old_r.end.col + 1; i <= r->end.col; i++)
1046 gnm_filter_add_field (filter, i - start);
1050 * gnm_sheet_filter_insdel_colrow :
1051 * @sheet:
1052 * @is_cols:
1053 * @is_insert:
1054 * @start:
1055 * @count:
1056 * @pundo: location to store undo closures.
1058 * Adjust filters as necessary to handle col/row insertions and deletions
1060 void
1061 gnm_sheet_filter_insdel_colrow (Sheet *sheet,
1062 gboolean is_cols, gboolean is_insert,
1063 int start, int count,
1064 GOUndo **pundo)
1066 GSList *ptr, *filters;
1068 g_return_if_fail (IS_SHEET (sheet));
1070 filters = g_slist_copy (sheet->filters);
1071 for (ptr = filters; ptr != NULL ; ptr = ptr->next) {
1072 GnmFilter *filter = ptr->data;
1073 gboolean kill_filter = FALSE;
1074 gboolean reapply_filter = FALSE;
1075 GnmRange r = filter->r;
1077 if (is_cols) {
1078 if (start > filter->r.end.col) /* a */
1079 continue;
1081 sheet->priv->filters_changed = TRUE;
1083 if (is_insert) {
1084 /* INSERTING COLUMNS */
1085 filter->r.end.col += count;
1086 /* inserting in the middle of a filter adds
1087 * fields. Everything else just moves it */
1088 if (start > filter->r.start.col &&
1089 start <= filter->r.end.col) {
1090 int i;
1091 for (i = 0; i < count; i++)
1092 gnm_filter_add_field (filter,
1093 start - filter->r.start.col + i);
1094 } else
1095 filter->r.start.col += count;
1096 } else {
1097 /* REMOVING COLUMNS */
1098 int start_del = start - filter->r.start.col;
1099 int end_del = start_del + count;
1100 if (start_del <= 0) {
1101 start_del = 0;
1102 if (end_del > 0)
1103 filter->r.start.col = start; /* c */
1104 else
1105 filter->r.start.col -= count; /* b */
1106 filter->r.end.col -= count;
1107 } else {
1108 if ((unsigned)end_del > filter->fields->len) {
1109 end_del = filter->fields->len;
1110 filter->r.end.col = start - 1; /* d */
1111 } else
1112 filter->r.end.col -= count;
1115 if (filter->r.end.col < filter->r.start.col)
1116 kill_filter = TRUE;
1117 else {
1118 while (end_del-- > start_del) {
1119 remove_col (filter, end_del, pundo);
1120 reapply_filter = TRUE;
1124 } else {
1125 if (start > filter->r.end.row)
1126 continue;
1128 sheet->priv->filters_changed = TRUE;
1130 if (is_insert) {
1131 /* INSERTING ROWS */
1132 filter->r.end.row += count;
1133 if (start < filter->r.start.row)
1134 filter->r.start.row += count;
1135 } else {
1136 /* REMOVING ROWS */
1137 if (start <= filter->r.start.row) {
1138 filter->r.end.row -= count;
1139 if (start + count > filter->r.start.row)
1140 /* delete if the dropdowns are wiped */
1141 filter->r.start.row = filter->r.end.row + 1;
1142 else
1143 filter->r.start.row -= count;
1144 } else if (start + count > filter->r.end.row)
1145 filter->r.end.row = start -1;
1146 else
1147 filter->r.end.row -= count;
1149 if (filter->r.end.row < filter->r.start.row)
1150 kill_filter = TRUE;
1154 if (kill_filter) {
1156 * Empty the filter as we need fresh combo boxes
1157 * if we undo.
1159 while (filter->fields->len)
1160 remove_col (filter,
1161 filter->fields->len - 1,
1162 pundo);
1164 /* Restore the filters range */
1165 gnm_filter_remove (filter);
1166 filter->r = r;
1168 if (pundo) {
1169 GOUndo *u = go_undo_binary_new
1170 (gnm_filter_ref (filter),
1171 sheet,
1172 (GOUndoBinaryFunc)gnm_filter_attach,
1173 (GFreeFunc)gnm_filter_unref,
1174 NULL);
1175 *pundo = go_undo_combine (*pundo, u);
1177 gnm_filter_unref (filter);
1178 } else if (reapply_filter) {
1179 GnmRange *range = g_new (GnmRange, 1);
1180 *range = r;
1181 if (pundo) {
1182 GOUndo *u = go_undo_binary_new
1183 (gnm_filter_ref (filter),
1184 range,
1185 (GOUndoBinaryFunc)gnm_filter_set_range,
1186 (GFreeFunc)gnm_filter_unref,
1187 g_free);
1188 *pundo = go_undo_combine (*pundo, u);
1190 gnm_filter_update_active (filter);
1191 gnm_filter_reapply (filter);
1195 g_slist_free (filters);