sheet-filter.c 34.9 KB
Newer Older
1 2 3 4 5
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 * filter.c: support for filters
 *
6
 * Copyright (C) 2002-2004 Jody Goldberg (jody@gnome.org)
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */
#include <gnumeric-config.h>
#include "sheet-filter.h"

25
#include "workbook.h"
26
#include "sheet.h"
27
#include "sheet-private.h"
28 29 30 31 32 33 34 35 36
#include "cell.h"
#include "expr.h"
#include "value.h"
#include "sheet-control-gui.h"
#include "sheet-object-impl.h"
#include "gnumeric-pane.h"
#include "gnumeric-canvas.h"
#include "dependent.h"
#include "ranges.h"
37 38
#include "str.h"
#include "number-match.h"
39
#include "dialogs.h"
40
#include "regutf8.h"
41
#include "style-color.h"
42 43 44

#include <libfoocanvas/foo-canvas-widget.h>
#include <gtk/gtk.h>
Jon Kåre Hellan's avatar
Jon Kåre Hellan committed
45
#include <gdk/gdkevents.h>
46 47
#include <gdk/gdkkeysyms.h>
#include <gsf/gsf-impl-utils.h>
48
#include <glib/gi18n.h>
49 50 51 52

typedef struct {
	SheetObject parent;

53
	GnmFilterCondition *cond;
54
	GnmFilter   	   *filter;
55 56 57 58 59 60 61 62 63
} GnmFilterField;

typedef struct {
	SheetObjectClass s_object_class;
} GnmFilterFieldClass;

#define FILTER_FIELD_TYPE     (filter_field_get_type ())
#define FILTER_FIELD(obj)     (G_TYPE_CHECK_INSTANCE_CAST((obj), FILTER_FIELD_TYPE, GnmFilterField))

64 65 66
#define	VIEW_ITEM_ID	"view-item"
#define ARROW_ID	"arrow"
#define	FIELD_ID	"field"
67
#define	WBCG_ID		"wbcg"
68 69 70

static GType filter_field_get_type (void);

71 72

GnmFilterCondition *
Jody Goldberg's avatar
Jody Goldberg committed
73
gnm_filter_condition_new_single (GnmFilterOp op, GnmValue *v)
74
{
75 76 77 78
	GnmFilterCondition *res = g_new0 (GnmFilterCondition, 1);
	res->op[0] = op;	res->op[1] = GNM_FILTER_UNUSED;
	res->value[0] = v;
	return res;
79 80 81
}

GnmFilterCondition *
Jody Goldberg's avatar
Jody Goldberg committed
82
gnm_filter_condition_new_double (GnmFilterOp op0, GnmValue *v0,
83
				 gboolean join_with_and,
Jody Goldberg's avatar
Jody Goldberg committed
84
				 GnmFilterOp op1, GnmValue *v1)
85
{
86 87 88 89 90
	GnmFilterCondition *res = g_new0 (GnmFilterCondition, 1);
	res->op[0] = op0;	res->op[1] = op1;
	res->is_and = join_with_and;
	res->value[0] = v0;	res->value[1] = v1;
	return res;
91 92 93 94 95
}

GnmFilterCondition *
gnm_filter_condition_new_bucket (gboolean top, gboolean absolute, unsigned n)
{
96 97 98 99
	GnmFilterCondition *res = g_new0 (GnmFilterCondition, 1);
	res->op[0] = GNM_FILTER_OP_TOP_N | (top ? 0 : 1) | (absolute ? 0 : 2);
	res->count = n;
	return res;
100 101 102 103 104
}

void
gnm_filter_condition_unref (GnmFilterCondition *cond)
{
105 106 107 108 109
	g_return_if_fail (cond != NULL);
	if (cond->value[0] != NULL)
		value_release (cond->value[0]);
	if (cond->value[1] != NULL)
		value_release (cond->value[1]);
110 111 112 113
}

/**********************************************************************************/

Jody Goldberg's avatar
fix.  
Jody Goldberg committed
114 115 116 117 118 119 120
static int
filter_field_index (GnmFilterField const *field)
{
	return field->parent.anchor.cell_bound.start.col -
		field->filter->r.start.col;
}

121 122 123
static void
filter_field_finalize (GObject *object)
{
124
	GnmFilterField *field = FILTER_FIELD (object);
125 126
	GObjectClass *parent;

127 128 129 130 131 132
	g_return_if_fail (field != NULL);

	if (field->cond != NULL) {
		gnm_filter_condition_unref (field->cond);
		field->cond = NULL;
	}
133 134

	parent = g_type_class_peek (SHEET_OBJECT_TYPE);
135
	parent->finalize (object);
136 137
}

138 139
/* Cut and paste from gtkwindow.c */
static void
140
do_focus_change (GtkWidget *widget, gboolean in)
141
{
142
	GdkEventFocus fevent;
Morten Welinder's avatar
Morten Welinder committed
143

144
	g_object_ref (widget);
Morten Welinder's avatar
Morten Welinder committed
145

146 147 148 149
	if (in)
		GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
	else
		GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
Morten Welinder's avatar
Morten Welinder committed
150

151 152 153
	fevent.type = GDK_FOCUS_CHANGE;
	fevent.window = widget->window;
	fevent.in = in;
Morten Welinder's avatar
Morten Welinder committed
154

155
	gtk_widget_event (widget, (GdkEvent *)&fevent);
Morten Welinder's avatar
Morten Welinder committed
156

157 158 159 160 161
	g_object_notify (G_OBJECT (widget), "has_focus");

	g_object_unref (widget);
}

162 163
static void
filter_popup_destroy (GtkWidget *popup, GtkWidget *list)
164 165 166 167 168
{
	do_focus_change (list, FALSE);
	gtk_widget_destroy (popup);
}

Morten Welinder's avatar
Morten Welinder committed
169
static gint
170
cb_filter_key_press (GtkWidget *popup, GdkEventKey *event, GtkWidget *list)
171 172 173
{
	if (event->keyval != GDK_Escape)
		return FALSE;
174
	filter_popup_destroy (popup, list);
175 176 177
	return TRUE;
}

Morten Welinder's avatar
Morten Welinder committed
178
static gint
179
cb_filter_button_press (GtkWidget *popup, GdkEventButton *event,
180
			GtkWidget *list)
181
{
182
	/* A press outside the popup cancels */
183 184 185 186 187
	if (event->window != popup->window) {
		filter_popup_destroy (popup, list);
		return TRUE;
	}
	return FALSE;
188 189
}

Morten Welinder's avatar
Morten Welinder committed
190
static gint
191
cb_filter_button_release (GtkWidget *popup, GdkEventButton *event,
192
			  GtkTreeView *list)
193
{
194 195
	GtkTreeIter  iter;
	GnmFilterField *field;
Jody Goldberg's avatar
Jody Goldberg committed
196
	GnmFilterCondition *cond = NULL;
197
	WorkbookControlGUI *wbcg;
198
	GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent *) event);
Jody Goldberg's avatar
fix.  
Jody Goldberg committed
199
	int field_num;
200

201 202 203
	/* A release inside list accepts */
	if (event_widget != GTK_WIDGET (list))
		return FALSE;
204 205

	field = g_object_get_data (G_OBJECT (list), FIELD_ID);
206
	wbcg  = g_object_get_data (G_OBJECT (list), WBCG_ID);
207 208 209 210
	if (field != NULL &&
	    gtk_tree_selection_get_selected (gtk_tree_view_get_selection (list),
					     NULL, &iter)) {
		char	*label;
Jody Goldberg's avatar
Jody Goldberg committed
211
		GnmValue   *val;
212 213 214 215 216 217 218
		int	 type;
		gboolean set_condition = TRUE;

		gtk_tree_model_get (gtk_tree_view_get_model (list), &iter,
				    0, &label, 1, &val, 2, &type,
				    -1);

Jody Goldberg's avatar
fix.  
Jody Goldberg committed
219
		field_num = filter_field_index (field);
220
		switch (type) {
221
		case  0 : cond = gnm_filter_condition_new_single (
222
				  GNM_FILTER_OP_EQUAL, value_dup (val));
223 224 225 226
			break;
		case  1 : cond = NULL;	break; /* unfilter */
		case  2 : /* Custom */
			set_condition = FALSE;
Jody Goldberg's avatar
fix.  
Jody Goldberg committed
227
			dialog_auto_filter (wbcg, field->filter, field_num,
228
					    TRUE, field->cond);
229 230 231 232 233 234 235 236 237
			break;
		case  3 : cond = gnm_filter_condition_new_single (
				  GNM_FILTER_OP_BLANKS, NULL);
			break;
		case  4 : cond = gnm_filter_condition_new_single (
				  GNM_FILTER_OP_NON_BLANKS, NULL);
			break;
		case 10 : /* Top 10 */
			set_condition = FALSE;
Jody Goldberg's avatar
fix.  
Jody Goldberg committed
238
			dialog_auto_filter (wbcg, field->filter, field_num,
239
					    FALSE, field->cond);
240
			break;
241

242 243 244 245 246 247 248
		default :
			set_condition = FALSE;
			g_warning ("Unknown type %d", type);
		};

		g_free (label);

249
		if (set_condition) {
Jody Goldberg's avatar
fix.  
Jody Goldberg committed
250
			gnm_filter_set_condition (field->filter, field_num,
Jody Goldberg's avatar
Jody Goldberg committed
251
						  cond, TRUE);
252
			sheet_update (field->filter->sheet);
253
		}
254
	}
255
	filter_popup_destroy (popup, GTK_WIDGET (list));
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
	return TRUE;
}

static gboolean
cb_filter_motion_notify_event (GtkWidget *widget, GdkEventMotion *event,
			       GtkTreeView *list)
{
	GtkTreePath *path;
	if (event->x >= 0 && event->y >= 0 &&
	    event->x < widget->allocation.width &&
	    event->y < widget->allocation.height &&
	    gtk_tree_view_get_path_at_pos (list, event->x, event->y,
					   &path, NULL, NULL, NULL)) {
		gtk_tree_selection_select_path (gtk_tree_view_get_selection (list), path);
		gtk_tree_path_free (path);
	}
	return TRUE;
}

typedef struct {
	gboolean has_blank;
	GHashTable *hash;
} UniqueCollection;

Jody Goldberg's avatar
Jody Goldberg committed
280
static GnmValue *
281
cb_collect_unique (Sheet *sheet, int col, int row, GnmCell *cell,
282 283
		   UniqueCollection *uc)
{
284
	if (cell_is_blank (cell))
285
		uc->has_blank = TRUE;
286
	else
287 288 289 290 291
		g_hash_table_replace (uc->hash, cell->value, cell->value);

	return NULL;
}
static void
Jody Goldberg's avatar
Jody Goldberg committed
292
cb_copy_hash_to_array (GnmValue *key, gpointer value, gpointer sorted)
293 294 295 296 297
{
	g_ptr_array_add (sorted, key);
}

static GtkListStore *
298 299
collect_unique_elements (GnmFilterField *field,
			 GtkTreePath **clip, GtkTreePath **select)
300 301 302 303 304 305
{
	UniqueCollection uc;
	GtkTreeIter	 iter;
	GtkListStore *model;
	GPtrArray    *sorted = g_ptr_array_new ();
	unsigned i;
306
	gboolean is_custom = FALSE;
Jody Goldberg's avatar
Jody Goldberg committed
307 308 309
	GnmRange	 r = field->filter->r;
	GnmValue const *check = NULL;
	GnmValue	    *check_num = NULL; /* XL stores numbers as string @$^!@$ */
310 311 312 313 314 315

	if (field->cond != NULL &&
	    field->cond->op[0] == GNM_FILTER_OP_EQUAL &&
	    field->cond->op[1] == GNM_FILTER_UNUSED) {
		check = field->cond->value[0];
		if (check->type == VALUE_STRING)
316 317
			check_num = format_match_number (check->v_str.val->str, NULL,
				workbook_date_conv (field->filter->sheet->workbook));
318
	}
319 320 321 322

	model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
	gtk_list_store_append (model, &iter);
	gtk_list_store_set (model, &iter, 0, _("(All)"),	   1, NULL, 2, 1, -1);
323 324 325
	if (field->cond == NULL || field->cond->op[0] == GNM_FILTER_UNUSED)
		*select = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);

326 327
	gtk_list_store_append (model, &iter);
	gtk_list_store_set (model, &iter, 0, _("(Top 10...)"),     1, NULL, 2, 10,-1);
328 329 330 331 332
	if (field->cond != NULL &&
	    (GNM_FILTER_OP_TYPE_MASK & field->cond->op[0]) == GNM_FILTER_OP_TOP_N)
		*select = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);

	/* default to this we can easily revamp later */
333 334
	gtk_list_store_append (model, &iter);
	gtk_list_store_set (model, &iter, 0, _("(Custom...)"),     1, NULL, 2, 2, -1);
335 336 337 338
	if (*select == NULL) {
		is_custom = TRUE;
		*select = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
	}
339

340 341
	r.start.row++;
	/* r.end.row =  XL actually extend to the first non-empty element in the list */
Jody Goldberg's avatar
fix.  
Jody Goldberg committed
342
	r.end.col = r.start.col += filter_field_index (field);
343
	uc.has_blank = FALSE;
344
#warning "FIXME: is this really the type of equality (and hash) we want here?"
345 346
	uc.hash = g_hash_table_new (
			(GHashFunc) value_hash, (GEqualFunc) value_equal);
347
	sheet_foreach_cell_in_range (field->filter->sheet,
348 349
		CELL_ITER_ALL,
		r.start.col, r.start.row, r.end.col, r.end.row,
350 351 352 353 354
		(CellIterFunc)&cb_collect_unique, &uc);

	g_hash_table_foreach (uc.hash,
		(GHFunc) cb_copy_hash_to_array, sorted);
	qsort (&g_ptr_array_index (sorted, 0),
Jody Goldberg's avatar
Jody Goldberg committed
355
	       sorted->len, sizeof (GnmValue *), value_cmp);
356
	for (i = 0; i < sorted->len ; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
357
		GnmValue const *v = g_ptr_array_index (sorted, i);
358 359 360
		gtk_list_store_append (model, &iter);
		gtk_list_store_set (model, &iter,
			0, value_peek_string (v),
Jody Goldberg's avatar
Jody Goldberg committed
361
			1, v,
362 363 364 365 366
			2, 0,
			-1);
		if (i == 10)
			*clip = gtk_tree_model_get_path (GTK_TREE_MODEL (model),
							 &iter);
367 368 369 370 371 372 373
		if (check != NULL) {
			if (value_compare (check, v, TRUE) == IS_EQUAL ||
			    (check_num != NULL && value_compare (check_num, v, TRUE) == IS_EQUAL)) {
				gtk_tree_path_free (*select);
				*select = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
			}
		}
374 375 376 377 378
	}

	if (uc.has_blank) {
		gtk_list_store_append (model, &iter);
		gtk_list_store_set (model, &iter, 0, _("(Blanks...)"),	   1, NULL, 2, 3, -1);
379 380 381 382
		if (field->cond != NULL &&
		    field->cond->op[0] == GNM_FILTER_OP_BLANKS)
			*select = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);

383 384
		gtk_list_store_append (model, &iter);
		gtk_list_store_set (model, &iter, 0, _("(Non Blanks...)"), 1, NULL, 2, 4, -1);
385 386 387 388 389 390 391
		if (field->cond != NULL &&
		    field->cond->op[0] == GNM_FILTER_OP_NON_BLANKS)
			*select = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
	} else if (is_custom && field->cond != NULL &&
		   (GNM_FILTER_OP_TYPE_MASK & field->cond->op[0]) == GNM_FILTER_OP_BLANKS) {
		gtk_tree_path_free (*select);
		*select = NULL;
392 393 394
	}

	g_hash_table_destroy (uc.hash);
395 396 397 398
	g_ptr_array_free (sorted, TRUE);

	if (check_num != NULL)
		value_release (check_num);
399 400 401 402

	return model;
}

Jody Goldberg's avatar
new.  
Jody Goldberg committed
403 404 405
static void
cb_focus_changed (GtkWindow *toplevel)
{
Yukihiro Nakai's avatar
Yukihiro Nakai committed
406
#if 0
Jody Goldberg's avatar
new.  
Jody Goldberg committed
407
	g_warning (gtk_window_has_toplevel_focus (toplevel) ? "focus" : "no focus");
Yukihiro Nakai's avatar
Yukihiro Nakai committed
408
#endif
Jody Goldberg's avatar
new.  
Jody Goldberg committed
409 410
}

411
static void
412
cb_filter_button_pressed (GtkButton *button, FooCanvasItem *view)
413
{
414
	GnmPane		*pane = GNM_CANVAS (view->canvas)->pane;
415
	SheetControlGUI *scg = pane->gcanvas->simple.scg;
416 417 418
	SheetObject	*so = sheet_object_view_get_so (SHEET_OBJECT_VIEW (view));
	GnmFilterField	*field = FILTER_FIELD (so);
	GtkWidget *frame, *popup, *list, *container;
419 420 421
	int root_x, root_y;
	GtkListStore  *model;
	GtkTreeViewColumn *column;
422
	GtkTreePath	  *clip = NULL, *select = NULL;
423 424 425
	GtkRequisition	req;

	popup = gtk_window_new (GTK_WINDOW_POPUP);
426
	model = collect_unique_elements (field, &clip, &select);
427 428 429 430 431 432 433
	column = gtk_tree_view_column_new_with_attributes ("ID",
			gtk_cell_renderer_text_new (), "text", 0,
			NULL);
	list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
	gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
	gtk_widget_size_request (GTK_WIDGET (list), &req);
434
	g_object_set_data (G_OBJECT (list), FIELD_ID, field);
435
	g_object_set_data (G_OBJECT (list), WBCG_ID, scg_get_wbcg (scg));
Jody Goldberg's avatar
new.  
Jody Goldberg committed
436 437 438
	g_signal_connect (G_OBJECT (wbcg_toplevel (scg_get_wbcg (scg))),
		"notify::has-toplevel-focus",
		G_CALLBACK (cb_focus_changed), list);
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456

	frame = gtk_frame_new (NULL);
	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);

	if (clip != NULL) {
		GdkRectangle  rect;
		GtkWidget *sw = gtk_scrolled_window_new (
			gtk_tree_view_get_hadjustment (GTK_TREE_VIEW (list)),
			gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (list)));
		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
						GTK_POLICY_AUTOMATIC,
						GTK_POLICY_ALWAYS);
		gtk_tree_view_get_background_area (GTK_TREE_VIEW (list),
						   clip, NULL, &rect);
		gtk_tree_path_free (clip);

		gtk_widget_set_size_request (list, req.width, rect.y);
		gtk_container_add (GTK_CONTAINER (sw), list);
457
		container = sw;
458
	} else
459 460 461
		container = list;

	gtk_container_add (GTK_CONTAINER (frame), container);
462 463 464 465 466 467 468 469

	/* do the popup */
	gtk_window_set_decorated (GTK_WINDOW (popup), FALSE);
	gdk_window_get_origin (GTK_WIDGET (pane->gcanvas)->window,
		&root_x, &root_y);
	gtk_window_move (GTK_WINDOW (popup),
		root_x + scg_colrow_distance_get (scg, TRUE,
			pane->gcanvas->first.col,
Jody Goldberg's avatar
fix.  
Jody Goldberg committed
470
			field->parent.anchor.cell_bound.start.col + 1) - req.width,
471 472 473 474 475 476 477 478
		root_y + scg_colrow_distance_get (scg, FALSE,
			pane->gcanvas->first.row,
			field->filter->r.start.row + 1));

	gtk_container_add (GTK_CONTAINER (popup), frame);

	g_signal_connect (popup,
		"key_press_event",
479
		G_CALLBACK (cb_filter_key_press), list);
480 481
	g_signal_connect (popup,
		"button_press_event",
482
		G_CALLBACK (cb_filter_button_press), list);
483 484
	g_signal_connect (popup,
		"button_release_event",
485
		G_CALLBACK (cb_filter_button_release), list);
486
	g_signal_connect (list,
487 488 489 490
		"motion_notify_event",
		G_CALLBACK (cb_filter_motion_notify_event), list);

	gtk_widget_show_all (popup);
491 492 493 494 495 496 497 498 499 500

	/* after we show the window setup the selection (showing the list
	 * clears the selection) */
	if (select != NULL) {
		gtk_tree_selection_select_path (
			gtk_tree_view_get_selection (GTK_TREE_VIEW (list)),
			select);
		gtk_tree_path_free (select);
	}

501 502 503
	gtk_widget_grab_focus (GTK_WIDGET (list));
	do_focus_change (GTK_WIDGET (list), TRUE);

504
	gtk_grab_add (popup);
505
	gdk_pointer_grab (popup->window, TRUE,
506
		GDK_BUTTON_PRESS_MASK |
507
		GDK_BUTTON_RELEASE_MASK |
508
		GDK_POINTER_MOTION_MASK,
509 510 511
		NULL, NULL, GDK_CURRENT_TIME);
}

512 513 514 515 516 517
static void
filter_field_arrow_format (GnmFilterField *field, GtkWidget *arrow)
{
	gtk_arrow_set (GTK_ARROW (arrow),
		field->cond != NULL ? GTK_ARROW_RIGHT : GTK_ARROW_DOWN,
		GTK_SHADOW_IN);
Jody Goldberg's avatar
Jody Goldberg committed
518 519
	gtk_widget_modify_fg (arrow, GTK_STATE_NORMAL,
		field->cond != NULL ? &gs_yellow : &gs_black);
520 521
}

522 523
static void
filter_view_destroy (SheetObjectView *sov)
524
{
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
	gtk_object_destroy (GTK_OBJECT (sov));
}
static void
filter_view_set_bounds (SheetObjectView *sov, double const *coords, gboolean visible)
{
	FooCanvasItem *view = FOO_CANVAS_ITEM (sov);

	if (visible) {
		double h, x;
		/* clip vertically */
		h = (coords[3] - coords[1]);
		if (h > 20.)
			h = 20.;

		/* maintain squareness horzontally */
		x = coords[2] - h;
		if (x < coords[0])
			x = coords[0];

		/* NOTE : far point is EXCLUDED so we add 1 */
		foo_canvas_item_set (view,
			"x",	x,
547 548
			"y",	coords [3] - h,
			"width",  coords [2] - x,
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
			"height", h + 1.,
			NULL);

		foo_canvas_item_show (view);
	} else
		foo_canvas_item_hide (view);
}

static void
filter_foo_view_init (SheetObjectViewIface *sov_iface)
{
	sov_iface->destroy	= filter_view_destroy;
	sov_iface->set_bounds	= filter_view_set_bounds;
}
typedef FooCanvasWidget		FilterFooView;
typedef FooCanvasWidgetClass	FilterFooViewClass;
static GSF_CLASS_FULL (FilterFooView, filter_foo_view,
	NULL, NULL,
	FOO_TYPE_CANVAS_WIDGET, 0,
	GSF_INTERFACE (filter_foo_view_init, SHEET_OBJECT_VIEW_TYPE))

static SheetObjectView *
filter_field_new_view (SheetObject *so, SheetObjectViewContainer *container)
{
	GnmCanvas *gcanvas = ((GnmPane *)container)->gcanvas;
574
	GtkWidget *arrow, *view_widget = gtk_button_new ();
575
	GnmFilterField *field = (GnmFilterField *) so;
576
	FooCanvasItem *view_item = foo_canvas_item_new (gcanvas->object_views,
577
		filter_foo_view_get_type (),
578 579 580 581 582
		"widget",	view_widget,
		"size_pixels",	FALSE,
		NULL);

	GTK_WIDGET_UNSET_FLAGS (view_widget, GTK_CAN_FOCUS);
583
	arrow = gtk_arrow_new (field->cond != NULL ? GTK_ARROW_RIGHT : GTK_ARROW_DOWN,
584
			       GTK_SHADOW_IN);
585
	filter_field_arrow_format (field, arrow);
586 587
	gtk_container_add (GTK_CONTAINER (view_widget), arrow);

588 589
	g_object_set_data (G_OBJECT (view_widget), VIEW_ITEM_ID, view_item);
	g_object_set_data (G_OBJECT (view_item), ARROW_ID, arrow);
590 591
	g_signal_connect (view_widget,
		"pressed",
592
		G_CALLBACK (cb_filter_button_pressed), view_item);
593
	gtk_widget_show_all (view_widget);
594 595

	return gnm_pane_object_register (so, view_item, FALSE);
596 597
}

598 599 600 601
static void
filter_field_init (SheetObject *so)
{
	/* keep the arrows from wandering with their cells */
602
	so->flags &= ~SHEET_OBJECT_MOVE_WITH_CELLS;
603 604
}

605 606 607 608 609 610 611 612 613 614
static void
filter_field_class_init (GObjectClass *object_class)
{
	SheetObjectClass *sheet_object_class = SHEET_OBJECT_CLASS (object_class);

	/* Object class method overrides */
	object_class->finalize = filter_field_finalize;

	/* SheetObject class method overrides */
	sheet_object_class->new_view	  = filter_field_new_view;
615 616
	sheet_object_class->read_xml_dom  = NULL;
	sheet_object_class->write_xml_dom = NULL;
617
	sheet_object_class->write_xml_sax = NULL;
618
	sheet_object_class->print         = NULL;
619
	sheet_object_class->copy          = NULL;
620 621 622
}

GSF_CLASS (GnmFilterField, filter_field,
623 624
	   filter_field_class_init, filter_field_init,
	   SHEET_OBJECT_TYPE);
625

Jody Goldberg's avatar
Jody Goldberg committed
626 627
/*****************************************************************************/

628 629
typedef struct  {
	GnmFilterCondition const *cond;
Jody Goldberg's avatar
Jody Goldberg committed
630
	GnmValue		 *val[2];
631
	go_regex_t  regexp[2];
632
	GnmDateConventions const *date_conv;
633 634 635
} FilterExpr;

static void
636 637 638
filter_expr_init (FilterExpr *fexpr, unsigned i,
		  GnmFilterCondition const *cond,
		  GnmFilter const *filter)
639
{
Jody Goldberg's avatar
Jody Goldberg committed
640
	GnmValue *tmp = cond->value[i];
641 642
	fexpr->date_conv = workbook_date_conv (filter->sheet->workbook);

643 644 645
	if (VALUE_IS_STRING (tmp)) {
		GnmFilterOp op = cond->op[i];
		char const *str = value_peek_string (tmp);
646 647
		fexpr->val[i] = format_match_number (str, NULL, fexpr->date_conv);
		if (fexpr->val[i] != NULL)
648 649
			return;
		if ((op == GNM_FILTER_OP_EQUAL || op == GNM_FILTER_OP_NOT_EQUAL) &&
650
		    gnumeric_regcomp_XL (fexpr->regexp + i,  str, REG_ICASE) == REG_OK)
651 652
			return;
	}
653
	fexpr->val[i] = value_dup (tmp);
654 655 656
}

static void
657
filter_expr_release (FilterExpr *fexpr, unsigned i)
658
{
659 660
	if (fexpr->val[i] != NULL)
		value_release (fexpr->val[i]);
661
	else
662
		go_regfree (fexpr->regexp + i);
663 664 665
}

static gboolean
666
filter_expr_eval (GnmFilterOp op, GnmValue const *src, go_regex_t const *regexp,
Jody Goldberg's avatar
Jody Goldberg committed
667
		  GnmValue *target)
668
{
669
	GnmValDiff cmp;
670 671 672 673 674

	if (src == NULL) {
		char const *str = value_peek_string (target);
		regmatch_t rm;

675
		switch (go_regexec (regexp, str, 1, &rm, 0)) {
676 677 678 679 680 681 682
		case REG_NOMATCH: return op == GNM_FILTER_OP_NOT_EQUAL;
		case REG_OK: return op == GNM_FILTER_OP_EQUAL;

		default:
			g_warning ("Unexpected regexec result");
			return FALSE;
		}
Morten Welinder's avatar
Morten Welinder committed
683
	}
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698

	cmp = value_compare (target, src, TRUE);
	switch (op) {
	case GNM_FILTER_OP_EQUAL     : return cmp == IS_EQUAL;
	case GNM_FILTER_OP_NOT_EQUAL : return cmp != IS_EQUAL;
	case GNM_FILTER_OP_GTE	: if (cmp == IS_EQUAL) return TRUE; /* fall */
	case GNM_FILTER_OP_GT	: return cmp == IS_GREATER;
	case GNM_FILTER_OP_LTE	: if (cmp == IS_EQUAL) return TRUE; /* fall */
	case GNM_FILTER_OP_LT	: return cmp == IS_LESS;
	default :
		g_warning ("Huh?");
		return FALSE;
	};
}

Jody Goldberg's avatar
Jody Goldberg committed
699
static GnmValue *
700
cb_filter_expr (Sheet *sheet, int col, int row, GnmCell *cell,
701
		FilterExpr const *fexpr)
702
{
703
	if (cell != NULL) {
704 705 706 707
		gboolean res = filter_expr_eval (fexpr->cond->op[0],
			fexpr->val[0], fexpr->regexp + 0, cell->value);
		if (fexpr->cond->op[1] != GNM_FILTER_UNUSED) {
			if (fexpr->cond->is_and && !res)
Morten Welinder's avatar
Morten Welinder committed
708
				goto nope;
709
			if (res && !fexpr->cond->is_and)
710
				return NULL;
711 712
			res = filter_expr_eval (fexpr->cond->op[1],
				fexpr->val[1], fexpr->regexp + 1, cell->value);
Morten Welinder's avatar
Morten Welinder committed
713
		}
714 715 716 717
		if (res)
			return NULL;
	}

Morten Welinder's avatar
Morten Welinder committed
718
 nope:
719
	colrow_set_visibility (sheet, FALSE, FALSE, row, row);
720 721 722
	return NULL;
}

Jody Goldberg's avatar
Jody Goldberg committed
723 724
/*****************************************************************************/

Jody Goldberg's avatar
Jody Goldberg committed
725
static GnmValue *
726
cb_filter_non_blanks (Sheet *sheet, int col, int row, GnmCell *cell, gpointer data)
727
{
728
	if (cell_is_blank (cell))
729
		colrow_set_visibility (sheet, FALSE, FALSE, row, row);
730 731 732
	return NULL;
}

Jody Goldberg's avatar
Jody Goldberg committed
733
static GnmValue *
734
cb_filter_blanks (Sheet *sheet, int col, int row, GnmCell *cell, gpointer data)
735
{
736
	if (!cell_is_blank (cell))
737
		colrow_set_visibility (sheet, FALSE, FALSE, row, row);
738 739 740
	return NULL;
}

Jody Goldberg's avatar
Jody Goldberg committed
741 742
/*****************************************************************************/

743 744 745 746
typedef struct {
	unsigned count;
	unsigned elements;
	gboolean find_max;
Jody Goldberg's avatar
Jody Goldberg committed
747
	GnmValue const **vals;
Jody Goldberg's avatar
Jody Goldberg committed
748
} FilterItems;
749

Jody Goldberg's avatar
Jody Goldberg committed
750
static GnmValue *
751
cb_filter_find_items (Sheet *sheet, int col, int row, GnmCell *cell,
752
		      FilterItems *data)
753
{
Jody Goldberg's avatar
Jody Goldberg committed
754
	GnmValue const *v = cell->value;
755 756
	if (data->elements >= data->count) {
		unsigned j, i = data->elements;
757
		GnmValDiff const cond = data->find_max ? IS_GREATER : IS_LESS;
758 759 760 761 762 763 764 765 766 767 768
		while (i-- > 0)
			if (value_compare (v, data->vals[i], TRUE) == cond) {
				for (j = 0; j < i ; j++)
					data->vals[j] = data->vals[j+1];
				data->vals[i] = v;
				break;
			}
	} else {
		data->vals [data->elements++] = v;
		if (data->elements == data->count) {
			qsort (data->vals, data->elements,
Jody Goldberg's avatar
Jody Goldberg committed
769
			       sizeof (GnmValue *),
Jody Goldberg's avatar
Jody Goldberg committed
770
			       data->find_max ? value_cmp : value_cmp_reverse);
771 772 773 774 775
		}
	}
	return NULL;
}

Jody Goldberg's avatar
Jody Goldberg committed
776
static GnmValue *
777
cb_hide_unwanted_items (Sheet *sheet, int col, int row, GnmCell *cell,
Jody Goldberg's avatar
Jody Goldberg committed
778
			FilterItems const *data)
779
{
780 781
	if (cell != NULL) {
		int i = data->elements;
Jody Goldberg's avatar
Jody Goldberg committed
782
		GnmValue const *v = cell->value;
Morten Welinder's avatar
Morten Welinder committed
783

784 785 786 787
		while (i-- > 0)
			if (data->vals[i] == v)
				return NULL;
	}
788 789 790 791
	colrow_set_visibility (sheet, FALSE, FALSE, row, row);
	return NULL;
}

Jody Goldberg's avatar
Jody Goldberg committed
792 793 794 795
/*****************************************************************************/

typedef struct {
	gboolean	initialized, find_max;
Jody Goldberg's avatar
Jody Goldberg committed
796
	gnm_float	low, high;
Jody Goldberg's avatar
Jody Goldberg committed
797 798
} FilterPercentage;

Jody Goldberg's avatar
Jody Goldberg committed
799
static GnmValue *
800
cb_filter_find_percentage (Sheet *sheet, int col, int row, GnmCell *cell,
Jody Goldberg's avatar
Jody Goldberg committed
801 802 803
			   FilterPercentage *data)
{
	if (VALUE_IS_NUMBER (cell->value)) {
Jody Goldberg's avatar
Jody Goldberg committed
804
		gnm_float const v = value_get_as_float (cell->value);
Jody Goldberg's avatar
Jody Goldberg committed
805 806 807 808 809 810 811 812 813 814 815 816 817 818

		if (data->initialized) {
			if (data->low > v)
				data->low = v;
			else if (data->high < v)
				data->high = v;
		} else {
			data->initialized = TRUE;
			data->low = data->high = v;
		}
	}
	return NULL;
}

Jody Goldberg's avatar
Jody Goldberg committed
819
static GnmValue *
820
cb_hide_unwanted_percentage (Sheet *sheet, int col, int row, GnmCell *cell,
Jody Goldberg's avatar
Jody Goldberg committed
821 822
			     FilterPercentage const *data)
{
823
	if (cell != NULL && VALUE_IS_NUMBER (cell->value)) {
Jody Goldberg's avatar
Jody Goldberg committed
824
		gnm_float const v = value_get_as_float (cell->value);
Jody Goldberg's avatar
Jody Goldberg committed
825 826 827 828 829 830 831 832 833 834 835 836 837
		if (data->find_max) {
			if (v >= data->high)
				return NULL;
		} else {
			if (v <= data->low)
				return NULL;
		}
	}
	colrow_set_visibility (sheet, FALSE, FALSE, row, row);
	return NULL;
}
/*****************************************************************************/

838 839 840 841
static void
filter_field_apply (GnmFilterField *field)
{
	GnmFilter *filter = field->filter;
Jody Goldberg's avatar
fix.  
Jody Goldberg committed
842
	int const col = field->parent.anchor.cell_bound.start.col;
843 844 845
	int const start_row = filter->r.start.row + 1;
	int const end_row = filter->r.end.row;

846 847 848
	if (start_row > end_row)
		return;

849 850 851
	if (field->cond == NULL ||
	    field->cond->op[0] == GNM_FILTER_UNUSED)
		return;
852 853 854
	if (0x10 >= (field->cond->op[0] & GNM_FILTER_OP_TYPE_MASK)) {
		FilterExpr data;
		data.cond = field->cond;
855
		filter_expr_init (&data, 0, field->cond, filter);
856
		if (field->cond->op[1] != GNM_FILTER_UNUSED)
857
			filter_expr_init (&data, 1, field->cond, field->filter);
858

859
		sheet_foreach_cell_in_range (filter->sheet,
860
			CELL_ITER_IGNORE_HIDDEN,
861
			col, start_row, col, end_row,
862 863 864 865 866 867
			(CellIterFunc) cb_filter_expr, &data);

		filter_expr_release (&data, 0);
		if (field->cond->op[1] != GNM_FILTER_UNUSED)
			filter_expr_release (&data, 1);
	} else if (field->cond->op[0] == GNM_FILTER_OP_BLANKS)
868
		sheet_foreach_cell_in_range (filter->sheet,
869 870 871 872
			CELL_ITER_IGNORE_HIDDEN,
			col, start_row, col, end_row,
			cb_filter_blanks, NULL);
	else if (field->cond->op[0] == GNM_FILTER_OP_NON_BLANKS)
873
		sheet_foreach_cell_in_range (filter->sheet,
874 875 876 877
			CELL_ITER_IGNORE_HIDDEN,
			col, start_row, col, end_row,
			cb_filter_non_blanks, NULL);
	else if (0x30 == (field->cond->op[0] & GNM_FILTER_OP_TYPE_MASK)) {
878
		if (field->cond->op[0] & 0x2) { /* relative */
Jody Goldberg's avatar
Jody Goldberg committed
879
			FilterPercentage data;
Jody Goldberg's avatar
Jody Goldberg committed
880
			gnm_float	 offset;
Jody Goldberg's avatar
Jody Goldberg committed
881 882 883

			data.find_max = (field->cond->op[0] & 0x1) ? FALSE : TRUE;
			data.initialized = FALSE;
884
			sheet_foreach_cell_in_range (filter->sheet,
Morten Welinder's avatar
Morten Welinder committed
885
				CELL_ITER_IGNORE_HIDDEN | CELL_ITER_IGNORE_BLANK,
Jody Goldberg's avatar
Jody Goldberg committed
886 887 888 889 890
				col, start_row, col, end_row,
				(CellIterFunc) cb_filter_find_percentage, &data);
			offset = (data.high - data.low) * field->cond->count / 100.;
			data.high -= offset;
			data.low  += offset;
891
			sheet_foreach_cell_in_range (filter->sheet,
Jody Goldberg's avatar
Jody Goldberg committed
892 893 894
				CELL_ITER_IGNORE_HIDDEN,
				col, start_row, col, end_row,
				(CellIterFunc) cb_hide_unwanted_percentage, &data);
895
		} else { /* absolute */
Jody Goldberg's avatar
Jody Goldberg committed
896
			FilterItems data;
897 898 899
			data.find_max = (field->cond->op[0] & 0x1) ? FALSE : TRUE;
			data.elements    = 0;
			data.count  = field->cond->count;
Jody Goldberg's avatar
Jody Goldberg committed
900
			data.vals   = g_alloca (sizeof (GnmValue *) * data.count);
901
			sheet_foreach_cell_in_range (filter->sheet,
902
				CELL_ITER_IGNORE_HIDDEN | CELL_ITER_IGNORE_BLANK,
903
				col, start_row, col, end_row,
Jody Goldberg's avatar
Jody Goldberg committed
904
				(CellIterFunc) cb_filter_find_items, &data);
905
			sheet_foreach_cell_in_range (filter->sheet,
906 907
				CELL_ITER_IGNORE_HIDDEN,
				col, start_row, col, end_row,
Jody Goldberg's avatar
Jody Goldberg committed
908
				(CellIterFunc) cb_hide_unwanted_items, &data);
909
		}
910 911 912 913 914 915 916 917 918 919 920
	} else
		g_warning ("Invalid operator %d", field->cond->op[0]);
}

static void
filter_field_set_active (GnmFilterField *field)
{
	GList *ptr;
	SheetObject *so = &field->parent;

	for (ptr = so->realized_list; ptr; ptr = ptr->next)
921 922
		filter_field_arrow_format (field,
			g_object_get_data (ptr->data, ARROW_ID));
923 924
}

925 926
/*************************************************************************/

Jody Goldberg's avatar
fix.  
Jody Goldberg committed
927 928 929 930 931 932 933 934 935 936 937
static void
gnm_filter_add_field (GnmFilter *filter, int i)
{
	/* pretend to fill the cell, then clip the X start later */
	static SheetObjectAnchorType const anchor_types [4] = {
		SO_ANCHOR_PERCENTAGE_FROM_COLROW_START,
		SO_ANCHOR_PERCENTAGE_FROM_COLROW_START,
		SO_ANCHOR_PERCENTAGE_FROM_COLROW_END,
		SO_ANCHOR_PERCENTAGE_FROM_COLROW_END
	};
	static float const offsets [4] = { .0, .0, 0., 0. };
938
	int n;
Jody Goldberg's avatar
Jody Goldberg committed
939
	GnmRange tmp;
Jody Goldberg's avatar
fix.  
Jody Goldberg committed
940 941 942 943 944 945 946 947
	SheetObjectAnchor anchor;
	GnmFilterField *field = g_object_new (filter_field_get_type (), NULL);

	field->filter = filter;
	tmp.start.row = tmp.end.row = filter->r.start.row;
	tmp.start.col = tmp.end.col = filter->r.start.col + i;
	sheet_object_anchor_init (&anchor, &tmp, offsets, anchor_types,
				  SO_DIR_DOWN_RIGHT);
948
	sheet_object_set_anchor (&field->parent, &anchor);
Jody Goldberg's avatar
fix.  
Jody Goldberg committed
949
	sheet_object_set_sheet (&field->parent, filter->sheet);
950 951

	g_ptr_array_add (filter->fields, NULL);
952
	for (n = filter->fields->len; --n > i ; )
953 954 955
		g_ptr_array_index (filter->fields, n) =
			g_ptr_array_index (filter->fields, n-1);
	g_ptr_array_index (filter->fields, n) = field;
Jody Goldberg's avatar
fix.  
Jody Goldberg committed
956 957 958
	g_object_unref (G_OBJECT (field));
}

959 960 961 962 963 964 965 966
/**
 * gnm_filter_new :
 * @sheet :
 * @r :
 *
 * Init a filter
 **/
GnmFilter *
Jody Goldberg's avatar
Jody Goldberg committed
967
gnm_filter_new (Sheet *sheet, GnmRange const *r)
968 969 970 971 972 973
{
	GnmFilter	*filter;
	int i;

	g_return_val_if_fail (IS_SHEET (sheet), NULL);
	g_return_val_if_fail (r != NULL, NULL);
Morten Welinder's avatar
Morten Welinder committed
974

975
	filter = g_new0 (GnmFilter, 1);
976
	filter->sheet = sheet;
977

978
	filter->is_active = FALSE;
979 980 981
	filter->r = *r;
	filter->fields = g_ptr_array_new ();

Jody Goldberg's avatar
fix.  
Jody Goldberg committed
982 983
	for (i = 0 ; i < range_width (r); i++)
		gnm_filter_add_field (filter, i);
984 985

	sheet->filters = g_slist_prepend (sheet->filters, filter);
986
	sheet->priv->filters_changed = TRUE;
987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009

	return filter;
}

void
gnm_filter_free	(GnmFilter *filter)
{
	unsigned i;

	g_return_if_fail (filter != NULL);

	for (i = 0 ; i < filter->fields->len ; i++)
		sheet_object_clear_sheet (g_ptr_array_index (filter->fields, i));
	g_ptr_array_free (filter->fields, TRUE);

	filter->fields = NULL;
	g_free (filter);
}

void
gnm_filter_remove (GnmFilter *filter)
{
	Sheet *sheet;
1010
	int i;
1011 1012

	g_return_if_fail (filter != NULL);
1013
	g_return_if_fail (filter->sheet != NULL);
1014

1015
	sheet = filter->sheet;
1016
	sheet->priv->filters_changed = TRUE;
1017
	sheet->filters = g_slist_remove (sheet->filters, filter);
1018
	for (i = filter->r.start.row; ++i <= filter->r.end.row ; ) {
1019 1020 1021 1022 1023
		ColRowInfo *ri = sheet_row_get (sheet, i);
		if (ri != NULL) {
			ri->in_filter = FALSE;
			colrow_set_visibility (sheet, FALSE, TRUE, i, i);
		}
1024 1025 1026
	}
}

Jody Goldberg's avatar
Jody Goldberg committed
1027 1028 1029 1030 1031 1032 1033
/**
 * gnm_filter_get_condition :
 * @filter :
 * @i :
 *
 **/
GnmFilterCondition const *
1034
gnm_filter_get_condition (GnmFilter const *filter, unsigned i)
Jody Goldberg's avatar
Jody Goldberg committed
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
{
	GnmFilterField *field;

	g_return_val_if_fail (filter != NULL, NULL);
	g_return_val_if_fail (i < filter->fields->len, NULL);

	field = g_ptr_array_index (filter->fields, i);
	return field->cond;
}

/**
 * gnm_filter_set_condition :
 * @filter :
 * @i :
 * @cond :
 * @apply :
 *
 **/
1053 1054 1055 1056 1057
void
gnm_filter_set_condition (GnmFilter *filter, unsigned i,
			  GnmFilterCondition *cond,
			  gboolean apply)
{
Jody Goldberg's avatar
Jody Goldberg committed
1058
	GnmFilterField *field;
1059
	gboolean set_infilter = FALSE;
1060
	gboolean existing_cond = FALSE;
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
	int r;

	g_return_if_fail (filter != NULL);
	g_return_if_fail (i < filter->fields->len);

	field = g_ptr_array_index (filter->fields, i);

	if (field->cond != NULL) {
		existing_cond = TRUE;
		gnm_filter_condition_unref (field->cond);
	}
	field->cond = cond;
	filter_field_set_active (field);

	if (apply) {
		/* if there was an existing cond then we need to do
		 * 1) unfilter everything
		 * 2) reapply all the filters
		 * This is because we do record what elements this particular
		 * field filtered
		 */
		if (existing_cond) {
1083 1084
			colrow_set_visibility (filter->sheet, FALSE, TRUE,
				filter->r.start.row + 1, filter->r.end.row);
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
			for (i = 0 ; i < filter->fields->len ; i++)
				filter_field_apply (g_ptr_array_index (filter->fields, i));
		} else
			/* When adding a new cond all we need to do is
			 * apply that filter */
			filter_field_apply (field);
	}

	/* set the activity flag and potentially activate the
	 * in_filter flags in the rows */
	if (cond == NULL) {
		for (i = 0 ; i < filter->fields->len ; i++) {
			field = g_ptr_array_index (filter->fields, i);
			if (field->cond != NULL)
				break;
		}
		if (i >= filter->fields->len) {
			filter->is_active = FALSE;
			set_infilter = TRUE;
		}
	} else if (!filter->is_active) {
		filter->is_active = TRUE;
		set_infilter = TRUE;
	}

	if (set_infilter)
		for (r = filter->r.start.row; ++r <= filter->r.end.row ; ) {
1112
			ColRowInfo *ri = sheet_row_fetch (filter->sheet, r);
1113 1114
			ri->in_filter = filter->is_active;
		}
1115 1116 1117
}

/**
1118 1119
 * gnm_filter_overlaps_range :
 * @filter : #GnmFilter
Jody Goldberg's avatar
Jody Goldberg committed
1120
 * @r : #GnmRange
1121
 *
Jody Goldberg's avatar
Jody Goldberg committed
1122
 * Does the range filter by @filter overlap with GnmRange @r
1123 1124
 **/
gboolean
Jody Goldberg's avatar
Jody Goldberg committed
1125
gnm_filter_overlaps_range (GnmFilter const *filter, GnmRange const *r)
1126 1127 1128
{
	g_return_val_if_fail (filter != NULL, FALSE);

1129
	return range_overlap (&filter->r, r);
1130
}
Morten Welinder's avatar
Morten Welinder committed
1131

1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
/*************************************************************************/

static gboolean
sheet_cell_or_one_below_is_not_empty (Sheet *sheet, int col, int row)
{
	return !sheet_is_cell_empty (sheet, col, row) ||
		(row < (SHEET_MAX_ROWS - 1) &&
		 !sheet_is_cell_empty (sheet, col, row+1));
}

/**
 * sheet_filter_guess_region :
 * @sheet : #Sheet
Jody Goldberg's avatar
Jody Goldberg committed
1145
 * @range : #GnmRange
1146 1147 1148
 *
 **/
void
Jody Goldberg's avatar
Jody Goldberg committed
1149
sheet_filter_guess_region (Sheet *sheet, GnmRange *region)
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175