dialog-autofilter.c 9.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/**
 * dialog-autofilter.c:  A pair of dialogs for autofilter conditions
 *
 * (c) Copyright 2002 Jody Goldberg <jody@gnome.org>
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
21

22
#include <gnumeric-config.h>
23
#include <glib/gi18n-lib.h>
24 25
#include <gnumeric.h>
#include "dialogs.h"
26
#include "help.h"
27 28 29 30 31 32 33

#include <gui-util.h>
#include <commands.h>
#include <workbook-control.h>
#include <workbook.h>
#include <workbook-edit.h>
#include <sheet.h>
34
#include <value.h>
35
#include <sheet-filter.h>
36
#include <number-match.h>
37 38

#include <glade/glade.h>
39
#include <gtk/gtkcombobox.h>
40 41
#include <gtk/gtktogglebutton.h>
#include <gtk/gtkspinbutton.h>
Jody Goldberg's avatar
Jody Goldberg committed
42
#include <string.h>
43 44 45

typedef struct {
	GladeXML           *gui;
46
	WBCGtk *wbcg;
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
	GtkWidget          *dialog;
	GnmFilter	   *filter;
	unsigned	    field;
	gboolean	    is_expr;
} AutoFilterState;

#define DIALOG_KEY "autofilter"

static void
cb_autofilter_destroy (AutoFilterState *state)
{
	wbcg_edit_detach_guru (state->wbcg);

	if (state->gui != NULL) {
		g_object_unref (G_OBJECT (state->gui));
		state->gui = NULL;
	}

	state->dialog = NULL;
	g_free (state);
}

Jody Goldberg's avatar
Jody Goldberg committed
69
static GnmValue *
70 71 72 73 74 75
map_op (AutoFilterState *state, GnmFilterOp *op,
	char const *op_widget, char const *val_widget)
{
	int i;
	GtkWidget *w = glade_xml_get_widget (state->gui, val_widget);
	char const *txt = gtk_entry_get_text (GTK_ENTRY (w));
Jody Goldberg's avatar
Jody Goldberg committed
76
	GnmValue *v = NULL;
77 78 79 80 81 82

	*op = GNM_FILTER_UNUSED;
	if (txt == NULL || *txt == '\0')
		return NULL;

	w = glade_xml_get_widget (state->gui, op_widget);
83
	i = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
84 85 86 87 88 89 90 91 92
	switch (i) {
	case 0: return NULL;
	case 1: *op = GNM_FILTER_OP_EQUAL;	break;
	case 2: *op = GNM_FILTER_OP_NOT_EQUAL;	break;
	case 3: *op = GNM_FILTER_OP_GT;		break;
	case 4: *op = GNM_FILTER_OP_GTE;	break;
	case 5: *op = GNM_FILTER_OP_LT;		break;
	case 6: *op = GNM_FILTER_OP_LTE;	break;

Morten Welinder's avatar
Morten Welinder committed
93
	case 7:
94
	case 8: *op = (i == 8) ? GNM_FILTER_OP_NOT_EQUAL : GNM_FILTER_OP_EQUAL;
95
		v = value_new_string_nocopy (g_strconcat (txt, "*", NULL));
96 97
		break;

Morten Welinder's avatar
Morten Welinder committed
98
	case 9:
99
	case 10: *op = (i == 10) ? GNM_FILTER_OP_NOT_EQUAL : GNM_FILTER_OP_EQUAL;
100
		v = value_new_string_nocopy (g_strconcat ("*", txt, NULL));
101 102
		break;

Morten Welinder's avatar
Morten Welinder committed
103
	case 11:
104 105 106 107 108 109
	case 12: *op = (i == 12) ? GNM_FILTER_OP_NOT_EQUAL : GNM_FILTER_OP_EQUAL;
		v = value_new_string_nocopy (g_strconcat ("*", txt, "*", NULL));
		break;
	default :
		g_warning ("huh?");
		return NULL;
110
	}
111

112
	if (v == NULL) {
113
		Workbook *wb = wb_control_get_workbook (WORKBOOK_CONTROL (state->wbcg));
114 115
		v = format_match (txt, NULL, workbook_date_conv (wb));
	}
116 117 118 119 120 121
	if (v == NULL)
		v = value_new_string (txt);

	return v;
}

122
static void
123
cb_autofilter_ok (G_GNUC_UNUSED GtkWidget *button,
124 125 126 127 128 129
		  AutoFilterState *state)
{
	GnmFilterCondition *cond = NULL;
	GtkWidget *w;

	if (state->is_expr) {
130
		GnmFilterOp op0;
Jody Goldberg's avatar
Jody Goldberg committed
131
		GnmValue *v0 = map_op (state, &op0, "op0", "value0");
132

133 134
		if (op0 != GNM_FILTER_UNUSED) {
			GnmFilterOp op1;
Jody Goldberg's avatar
Jody Goldberg committed
135
			GnmValue *v1 = map_op (state, &op1, "op1", "value1");
136 137 138 139 140 141 142 143
			if (op1 != GNM_FILTER_UNUSED) {
				w = glade_xml_get_widget (state->gui, "and_button");
				cond = gnm_filter_condition_new_double (op0, v0,
						gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)),
						op1, v1);
			} else
				cond = gnm_filter_condition_new_single (op0, v0);
		}
144 145 146 147
	} else {
		int bottom, percentage, count;

		w = glade_xml_get_widget (state->gui, "top_vs_bottom_option_menu");
148
		bottom = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
149 150

		w = glade_xml_get_widget (state->gui, "item_vs_percentage_option_menu");
151
		percentage = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
152 153 154 155 156 157 158 159 160

		w = glade_xml_get_widget (state->gui, "item_count");
		count = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w));
		if (bottom >= 0 && percentage >= 0)
			cond = gnm_filter_condition_new_bucket (
					!bottom, !percentage, count);
	}
	if (cond != NULL) {
		gnm_filter_set_condition (state->filter, state->field,
161
			cond, TRUE);
162
		sheet_update (state->filter->sheet);
163 164 165 166 167 168
	}

	gtk_widget_destroy (state->dialog);
}

static void
169 170
cb_autofilter_cancel (G_GNUC_UNUSED GtkWidget *button,
		      AutoFilterState *state)
171 172 173 174
{
	gtk_widget_destroy (state->dialog);
}

175
static void
176
cb_top10_type_changed (GtkComboBox *menu,
177 178 179 180
		       AutoFilterState *state)
{
	GtkWidget *spin = glade_xml_get_widget (state->gui, "item_count");

181
	gtk_spin_button_set_range (GTK_SPIN_BUTTON (spin), 1.,
182
		(gtk_combo_box_get_active (menu) > 0) ? 100. : 500.);
183 184
}

185
static void
Jody Goldberg's avatar
Jody Goldberg committed
186
init_operator (AutoFilterState *state, GnmFilterOp op, GnmValue const *v,
187 188 189
	       char const *op_widget, char const *val_widget)
{
	GtkWidget *w = glade_xml_get_widget (state->gui, op_widget);
190 191
	char const *str = v ? value_peek_string (v) : NULL;
	char *content = NULL;
192 193 194 195 196 197 198 199 200 201 202
	int i;

	switch (op) {
	case GNM_FILTER_OP_EQUAL:	i = 1; break;
	case GNM_FILTER_OP_GT:		i = 3; break;
	case GNM_FILTER_OP_LT:		i = 5; break;
	case GNM_FILTER_OP_GTE:		i = 4; break;
	case GNM_FILTER_OP_LTE:		i = 6; break;
	case GNM_FILTER_OP_NOT_EQUAL:	i = 2; break;
	default :
		return;
203
	}
204

205
	if (v != NULL && VALUE_IS_STRING (v) && (i == 1 || i == 2)) {
206
		unsigned const len = strlen (str);
207

208
		/* there needs to be at least 1 letter */
209
		int ends = (len > 1 && str[0] == '*') ? 1 : 0; /* as a bool and offset */
210

211 212 213
		if (len > 1 && str[len-1] == '*' && str[len-2] != '~') {
			content = g_strdup (str + ends);
			content[len - ends - 1] = '\0';
214
			i += (ends ? 10 : 6);
215 216
		} else if (ends) {
			str += 1;
217
			i += 8;
218
		}
219
	}
220
	gtk_combo_box_set_active (GTK_COMBO_BOX (w), i);
221

222 223 224
	w = glade_xml_get_widget (state->gui, val_widget);
	gnumeric_editable_enters (GTK_WINDOW (state->dialog), w);
	if (v != NULL)
225
		gtk_entry_set_text (GTK_ENTRY (w), content ? content : str);
226

227
	g_free (content);
228 229
}

230
void
231
dialog_auto_filter (WBCGtk *wbcg,
232 233 234 235 236
		    GnmFilter *filter, int field,
		    gboolean is_expr, GnmFilterCondition *cond)
{
	AutoFilterState *state;
	GtkWidget *w;
237
	GladeXML *gui;
238 239 240 241 242

	g_return_if_fail (wbcg != NULL);

	if (gnumeric_dialog_raise_if_exists (wbcg, DIALOG_KEY))
		return;
243
	gui = gnm_glade_xml_new (GO_CMD_CONTEXT (wbcg),
244 245 246 247
			(is_expr ? "autofilter-expression.glade" : "autofilter-top10.glade"),
			NULL, NULL);
	if (gui == NULL)
		return;
248 249 250 251 252 253

	state = g_new (AutoFilterState, 1);
	state->wbcg	= wbcg;
	state->filter	= filter;
	state->field	= field;
	state->is_expr	= is_expr;
254
	state->gui	= gui;
255 256 257

	g_return_if_fail (state->gui != NULL);

258 259 260 261 262 263 264 265
	if (is_expr) {
	} else {
		w = glade_xml_get_widget (state->gui, "item_vs_percentage_option_menu");
		g_signal_connect (G_OBJECT (w),
			"changed",
			G_CALLBACK (cb_top10_type_changed), state);
	}

266
	state->dialog = glade_xml_get_widget (state->gui, "dialog");
267 268 269
	if (cond != NULL) {
		GnmFilterOp const op = cond->op[0];
		if (is_expr && 0 == (op & GNM_FILTER_OP_TYPE_MASK)) {
270 271 272 273 274 275 276 277
			init_operator (state, cond->op[0],
				       cond->value[0], "op0", "value0");
			if (cond->op[1] != GNM_FILTER_UNUSED)
				init_operator (state, cond->op[1],
					       cond->value[1], "op1", "value1");
			w = glade_xml_get_widget (state->gui,
				cond->is_and ? "and_button" : "or_button");
			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), TRUE);
278 279 280
		} else if (!is_expr &&
			   GNM_FILTER_OP_TOP_N == (op & GNM_FILTER_OP_TYPE_MASK)) {
			w = glade_xml_get_widget (state->gui, "top_vs_bottom_option_menu");
281
			gtk_combo_box_set_active (GTK_COMBO_BOX (w), (op & 1) ? 1 : 0);
282
			w = glade_xml_get_widget (state->gui, "item_vs_percentage_option_menu");
283
			gtk_combo_box_set_active (GTK_COMBO_BOX (w), (op & 2) ? 1 : 0);
284 285 286 287
			w = glade_xml_get_widget (state->gui, "item_count");
			gtk_spin_button_set_value (GTK_SPIN_BUTTON (w),
						   cond->count);
		}
288 289 290 291 292 293 294 295 296 297 298 299 300
	} else {
		/* initialize the combo boxes (not done by libglade) */
		if (is_expr) {
			w = glade_xml_get_widget (state->gui, "op0");
			gtk_combo_box_set_active (GTK_COMBO_BOX (w), 0);
			w = glade_xml_get_widget (state->gui, "op1");
			gtk_combo_box_set_active (GTK_COMBO_BOX (w), 0);
		} else {
			w = glade_xml_get_widget (state->gui, "top_vs_bottom_option_menu");
			gtk_combo_box_set_active (GTK_COMBO_BOX (w), 0);
			w = glade_xml_get_widget (state->gui, "item_vs_percentage_option_menu");
			gtk_combo_box_set_active (GTK_COMBO_BOX (w), 0);
		}
301 302 303 304 305 306 307 308 309 310 311 312 313 314
	}

	w = glade_xml_get_widget (state->gui, "ok_button");
	g_signal_connect (G_OBJECT (w),
		"clicked",
		G_CALLBACK (cb_autofilter_ok), state);
	w = glade_xml_get_widget (state->gui, "cancel_button");
	g_signal_connect (G_OBJECT (w),
		"clicked",
		G_CALLBACK (cb_autofilter_cancel), state);

	/* a candidate for merging into attach guru */
	gnumeric_init_help_button (
		glade_xml_get_widget (state->gui, "help_button"),
315 316
		is_expr ? GNUMERIC_HELP_LINK_AUTOFILTER_CUSTOM :
		GNUMERIC_HELP_LINK_AUTOFILTER_TOP_TEN);
317 318 319 320 321 322
	g_object_set_data_full (G_OBJECT (state->dialog),
		"state", state, (GDestroyNotify)cb_autofilter_destroy);
	wbcg_edit_attach_guru (state->wbcg, state->dialog);
	gnumeric_keyed_dialog (wbcg, GTK_WINDOW (state->dialog), DIALOG_KEY);
	gtk_widget_show (state->dialog);
}