2 * dialog-autofilter.c: A pair of dialogs for autofilter conditions
4 * (c) Copyright 2002 Jody Goldberg <jody@gnome.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
30 #include <workbook-control.h>
37 #include <sheet-filter.h>
38 #include <number-match.h>
53 #define DIALOG_KEY "autofilter"
54 #define DIALOG_KEY_EXPRESSION "autofilter-expression"
55 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
57 static char const * const type_group
[] = {
61 "percentage-smallest",
62 "percentage-largest-number",
63 "percentage-smallest-number",
68 autofilter_get_type (AutoFilterState
*state
)
70 return (GNM_FILTER_OP_TYPE_BUCKETS
|
71 gnm_gui_group_value (state
->gui
, type_group
));
76 cb_autofilter_destroy (AutoFilterState
*state
)
78 if (state
->gui
!= NULL
) {
79 g_object_unref (state
->gui
);
88 map_op (AutoFilterState
*state
, GnmFilterOp
*op
,
89 char const *op_widget
, char const *val_widget
)
92 GtkWidget
*w
= go_gtk_builder_get_widget (state
->gui
, val_widget
);
93 char const *txt
= gtk_entry_get_text (GTK_ENTRY (w
));
96 *op
= GNM_FILTER_UNUSED
;
97 if (txt
== NULL
|| *txt
== '\0')
100 w
= go_gtk_builder_get_widget (state
->gui
, op_widget
);
101 i
= gtk_combo_box_get_active (GTK_COMBO_BOX (w
));
104 case 1: *op
= GNM_FILTER_OP_EQUAL
; break;
105 case 2: *op
= GNM_FILTER_OP_NOT_EQUAL
; break;
106 case 3: *op
= GNM_FILTER_OP_GT
; break;
107 case 4: *op
= GNM_FILTER_OP_GTE
; break;
108 case 5: *op
= GNM_FILTER_OP_LT
; break;
109 case 6: *op
= GNM_FILTER_OP_LTE
; break;
112 case 8: *op
= (i
== 8) ? GNM_FILTER_OP_NOT_EQUAL
: GNM_FILTER_OP_EQUAL
;
113 v
= value_new_string_nocopy (g_strconcat (txt
, "*", NULL
));
117 case 10: *op
= (i
== 10) ? GNM_FILTER_OP_NOT_EQUAL
: GNM_FILTER_OP_EQUAL
;
118 v
= value_new_string_nocopy (g_strconcat ("*", txt
, NULL
));
122 case 12: *op
= (i
== 12) ? GNM_FILTER_OP_NOT_EQUAL
: GNM_FILTER_OP_EQUAL
;
123 v
= value_new_string_nocopy (g_strconcat ("*", txt
, "*", NULL
));
131 Workbook
*wb
= wb_control_get_workbook (GNM_WBC (state
->wbcg
));
132 v
= format_match (txt
, NULL
, workbook_date_conv (wb
));
135 v
= value_new_string (txt
);
141 cb_autofilter_ok (G_GNUC_UNUSED GtkWidget
*button
,
142 AutoFilterState
*state
)
144 GnmFilterCondition
*cond
= NULL
;
147 if (state
->is_expr
) {
149 GnmValue
*v0
= map_op (state
, &op0
, "op0", "value0");
151 if (op0
!= GNM_FILTER_UNUSED
) {
153 GnmValue
*v1
= map_op (state
, &op1
, "op1", "value1");
154 if (op1
!= GNM_FILTER_UNUSED
) {
155 w
= go_gtk_builder_get_widget (state
->gui
,
157 cond
= gnm_filter_condition_new_double
159 gtk_toggle_button_get_active
160 (GTK_TOGGLE_BUTTON (w
)),
163 cond
= gnm_filter_condition_new_single
168 GnmFilterOp op
= autofilter_get_type (state
);
170 w
= go_gtk_builder_get_widget (state
->gui
, "item_count");
171 count
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (w
));
173 cond
= gnm_filter_condition_new_bucket
174 (!(op
& GNM_FILTER_OP_BOTTOM_MASK
),
175 !(op
& GNM_FILTER_OP_PERCENT_MASK
),
176 !(op
& GNM_FILTER_OP_REL_N_MASK
),
180 cmd_autofilter_set_condition (GNM_WBC (state
->wbcg
),
181 state
->filter
, state
->field
,
184 gtk_widget_destroy (state
->dialog
);
188 cb_autofilter_cancel (G_GNUC_UNUSED GtkWidget
*button
,
189 AutoFilterState
*state
)
191 gtk_widget_destroy (state
->dialog
);
195 cb_top10_count_changed (GtkSpinButton
*button
,
196 AutoFilterState
*state
)
198 int val
= 0.5 + gtk_spin_button_get_value (button
);
201 int cval
= val
, count
;
203 count
= range_height(&(state
->filter
->r
)) - 1;
208 w
= go_gtk_builder_get_widget (state
->gui
, type_group
[0]);
209 /* xgettext : %d gives the number of items in the autofilter. */
210 /* This is input to ngettext. */
211 label
= g_strdup_printf (ngettext ("Show the largest item",
212 "Show the %3d largest items",
215 gtk_button_set_label (GTK_BUTTON (w
),label
);
218 w
= go_gtk_builder_get_widget (state
->gui
, type_group
[1]);
219 /* xgettext : %d gives the number of items in the autofilter. */
220 /* This is input to ngettext. */
221 label
= g_strdup_printf (ngettext ("Show the smallest item",
222 "Show the %3d smallest items",
225 gtk_button_set_label (GTK_BUTTON (w
),label
);
231 w
= go_gtk_builder_get_widget (state
->gui
, type_group
[2]);
232 /* xgettext : %d gives the percentage of the data range in the autofilter. */
233 /* This is input to ngettext. */
234 label
= g_strdup_printf
235 (ngettext ("Show the items in the top %3d%% of the data range",
236 "Show the items in the top %3d%% of the data range", val
),
238 gtk_button_set_label (GTK_BUTTON (w
),label
);
241 w
= go_gtk_builder_get_widget (state
->gui
, type_group
[3]);
242 /* xgettext : %d gives the percentage of the data range in the autofilter. */
243 /* This is input to ngettext. */
244 label
= g_strdup_printf
245 (ngettext ("Show the items in the bottom %3d%% of the data range",
246 "Show the items in the bottom %3d%% of the data range", val
),
248 gtk_button_set_label (GTK_BUTTON (w
),label
);
252 w
= go_gtk_builder_get_widget (state
->gui
, type_group
[4]);
253 /* xgettext : %d gives the percentage of item number in the autofilter. */
254 /* This is input to ngettext. */
255 label
= g_strdup_printf
256 (ngettext ("Show the top %3d%% of all items",
257 "Show the top %3d%% of all items", val
),
259 gtk_button_set_label (GTK_BUTTON (w
),label
);
262 w
= go_gtk_builder_get_widget (state
->gui
, type_group
[5]);
263 /* xgettext : %d gives the percentage of the item number in the autofilter. */
264 /* This is input to ngettext. */
265 label
= g_strdup_printf
266 (ngettext ("Show the bottom %3d%% of all items",
267 "Show the bottom %3d%% of all items", val
),
269 gtk_button_set_label (GTK_BUTTON (w
),label
);
276 cb_top10_type_changed (G_GNUC_UNUSED GtkToggleButton
*button
,
277 AutoFilterState
*state
)
279 GnmFilterOp op
= autofilter_get_type (state
);
280 GtkWidget
*spin
= go_gtk_builder_get_widget (state
->gui
, "item_count");
281 GtkWidget
*label
= go_gtk_builder_get_widget (state
->gui
, "cp-label");
283 if ((op
& GNM_FILTER_OP_PERCENT_MASK
) != 0) {
284 gtk_spin_button_set_range (GTK_SPIN_BUTTON (spin
), 1.,
286 gtk_label_set_text (GTK_LABEL (label
), _("Percentage:"));
288 gtk_spin_button_set_range
289 (GTK_SPIN_BUTTON (spin
), 1.,
290 range_height(&(state
->filter
->r
)) - 1);
291 gtk_label_set_text (GTK_LABEL (label
), _("Count:"));
296 init_operator (AutoFilterState
*state
, GnmFilterOp op
, GnmValue
const *v
,
297 char const *op_widget
, char const *val_widget
)
299 GtkWidget
*w
= go_gtk_builder_get_widget (state
->gui
, op_widget
);
300 char const *str
= v
? value_peek_string (v
) : NULL
;
301 char *content
= NULL
;
305 case GNM_FILTER_OP_EQUAL
: i
= 1; break;
306 case GNM_FILTER_OP_GT
: i
= 3; break;
307 case GNM_FILTER_OP_LT
: i
= 5; break;
308 case GNM_FILTER_OP_GTE
: i
= 4; break;
309 case GNM_FILTER_OP_LTE
: i
= 6; break;
310 case GNM_FILTER_OP_NOT_EQUAL
: i
= 2; break;
315 if (v
!= NULL
&& VALUE_IS_STRING (v
) && (i
== 1 || i
== 2)) {
316 unsigned const len
= strlen (str
);
318 /* there needs to be at least 1 letter */
319 int ends
= (len
> 1 && str
[0] == '*') ? 1 : 0; /* as a bool and offset */
321 if (len
> 1 && str
[len
-1] == '*' && str
[len
-2] != '~') {
322 content
= g_strdup (str
+ ends
);
323 content
[len
- ends
- 1] = '\0';
324 i
+= (ends
? 10 : 6);
330 gtk_combo_box_set_active (GTK_COMBO_BOX (w
), i
);
332 w
= go_gtk_builder_get_widget (state
->gui
, val_widget
);
333 gnm_editable_enters (GTK_WINDOW (state
->dialog
), w
);
335 gtk_entry_set_text (GTK_ENTRY (w
), content
? content
: str
);
341 dialog_auto_filter_get_col_name (GnmCell
*cell
, int col
, int len
)
344 char *content
= gnm_cell_get_rendered_text (cell
);
345 if (g_utf8_strlen (content
, -1) > len
) {
346 char *end
= g_utf8_find_prev_char
347 (content
, content
+ len
+ 1 - strlen (UNICODE_ELLIPSIS
));
348 strcpy (end
, UNICODE_ELLIPSIS
);
350 label
= g_strdup_printf (_("Column %s (\"%s\")"),
351 col_name (col
), content
);
356 dialog_auto_filter_expression (WBCGtk
*wbcg
,
357 GnmFilter
*filter
, int field
,
358 GnmFilterCondition
*cond
)
360 AutoFilterState
*state
;
368 g_return_if_fail (wbcg
!= NULL
);
370 if (gnm_dialog_raise_if_exists
371 (wbcg
, DIALOG_KEY_EXPRESSION
))
373 gui
= gnm_gtk_builder_load ("res:ui/autofilter-expression.ui",
374 NULL
, GO_CMD_CONTEXT (wbcg
));
378 state
= g_new (AutoFilterState
, 1);
380 state
->filter
= filter
;
381 state
->field
= field
;
382 state
->is_expr
= TRUE
;
385 g_return_if_fail (state
->gui
!= NULL
);
387 col
= filter
->r
.start
.col
+ field
;
389 cell
= sheet_cell_get (filter
->sheet
, col
, filter
->r
.start
.row
);
391 if (cell
== NULL
|| gnm_cell_is_blank (cell
))
392 label
= g_strdup_printf (_("Column %s"), col_name (col
));
394 label
= dialog_auto_filter_get_col_name (cell
, col
, len
);
397 (GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "col-label1")), label
);
399 (GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "col-label2")), label
);
402 state
->dialog
= go_gtk_builder_get_widget (state
->gui
, "dialog");
404 GnmFilterOp
const op
= cond
->op
[0];
405 if (0 == (op
& GNM_FILTER_OP_TYPE_MASK
)) {
406 init_operator (state
, cond
->op
[0],
407 cond
->value
[0], "op0", "value0");
408 if (cond
->op
[1] != GNM_FILTER_UNUSED
)
409 init_operator (state
, cond
->op
[1],
410 cond
->value
[1], "op1", "value1");
411 w
= go_gtk_builder_get_widget (state
->gui
,
412 cond
->is_and
? "and_button" : "or_button");
413 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), TRUE
);
416 /* initialize the combo boxes (not done by li.ui) */
417 w
= go_gtk_builder_get_widget (state
->gui
, "op0");
418 gtk_combo_box_set_active (GTK_COMBO_BOX (w
), 0);
419 w
= go_gtk_builder_get_widget (state
->gui
, "op1");
420 gtk_combo_box_set_active (GTK_COMBO_BOX (w
), 0);
423 w
= go_gtk_builder_get_widget (state
->gui
, "ok_button");
424 g_signal_connect (G_OBJECT (w
),
426 G_CALLBACK (cb_autofilter_ok
), state
);
427 w
= go_gtk_builder_get_widget (state
->gui
, "cancel_button");
428 g_signal_connect (G_OBJECT (w
),
430 G_CALLBACK (cb_autofilter_cancel
), state
);
432 /* a candidate for merging into attach guru */
433 gnm_init_help_button (
434 go_gtk_builder_get_widget (state
->gui
, "help_button"),
435 GNUMERIC_HELP_LINK_AUTOFILTER_CUSTOM
);
437 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
),
439 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED
);
441 wbc_gtk_attach_guru (state
->wbcg
, state
->dialog
);
442 g_object_set_data_full (G_OBJECT (state
->dialog
),
443 "state", state
, (GDestroyNotify
)cb_autofilter_destroy
);
445 gnm_keyed_dialog (wbcg
, GTK_WINDOW (state
->dialog
),
446 DIALOG_KEY_EXPRESSION
);
447 gtk_widget_show (state
->dialog
);
451 dialog_auto_filter (WBCGtk
*wbcg
,
452 GnmFilter
*filter
, int field
,
453 gboolean is_expr
, GnmFilterCondition
*cond
)
455 AutoFilterState
*state
;
461 int len
= is_expr
? 15 : 30;
462 char const * const *rb
;
465 dialog_auto_filter_expression (wbcg
, filter
, field
, cond
);
469 g_return_if_fail (wbcg
!= NULL
);
471 if (gnm_dialog_raise_if_exists (wbcg
, DIALOG_KEY
))
473 gui
= gnm_gtk_builder_load ("res:ui/autofilter-top10.ui",
474 NULL
, GO_CMD_CONTEXT (wbcg
));
478 state
= g_new (AutoFilterState
, 1);
480 state
->filter
= filter
;
481 state
->field
= field
;
482 state
->is_expr
= FALSE
;
485 g_return_if_fail (state
->gui
!= NULL
);
487 col
= filter
->r
.start
.col
+ field
;
489 cell
= sheet_cell_get (filter
->sheet
, col
, filter
->r
.start
.row
);
491 if (cell
== NULL
|| gnm_cell_is_blank (cell
))
492 label
= g_strdup_printf (_("Column %s"), col_name (col
));
494 label
= dialog_auto_filter_get_col_name (cell
, col
, len
);
497 (GTK_LABEL (go_gtk_builder_get_widget (state
->gui
, "col-label")), label
);
500 state
->dialog
= go_gtk_builder_get_widget (state
->gui
, "dialog");
501 if (cond
!= NULL
&& GNM_FILTER_OP_TOP_N
== (cond
->op
[0] & GNM_FILTER_OP_TYPE_MASK
)) {
502 gchar
const *radio
= NULL
;
503 switch (cond
->op
[0]) {
504 case GNM_FILTER_OP_TOP_N
:
506 radio
= type_group
[0];
508 case GNM_FILTER_OP_BOTTOM_N
:
509 radio
= type_group
[1];
511 case GNM_FILTER_OP_TOP_N_PERCENT
:
512 radio
= type_group
[2];
514 case GNM_FILTER_OP_BOTTOM_N_PERCENT
:
515 radio
= type_group
[3];
517 case GNM_FILTER_OP_TOP_N_PERCENT_N
:
518 radio
= type_group
[4];
520 case GNM_FILTER_OP_BOTTOM_N_PERCENT_N
:
521 radio
= type_group
[5];
524 w
= go_gtk_builder_get_widget (state
->gui
, radio
);
525 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), TRUE
);
527 w
= go_gtk_builder_get_widget (state
->gui
, "items-largest");
528 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w
), TRUE
);
531 w
= go_gtk_builder_get_widget (state
->gui
, "item_count");
532 g_signal_connect (G_OBJECT (w
),
534 G_CALLBACK (cb_top10_count_changed
), state
);
535 if (cond
!= NULL
&& GNM_FILTER_OP_TOP_N
== (cond
->op
[0] & GNM_FILTER_OP_TYPE_MASK
))
536 gtk_spin_button_set_value (GTK_SPIN_BUTTON (w
), cond
->count
);
538 gtk_spin_button_set_value (GTK_SPIN_BUTTON (w
),
539 range_height(&(state
->filter
->r
))/2);
540 cb_top10_count_changed (GTK_SPIN_BUTTON (w
), state
);
541 cb_top10_type_changed (NULL
, state
);
544 while (*rb
!= NULL
) {
545 w
= go_gtk_builder_get_widget (state
->gui
, *rb
);
546 g_signal_connect (G_OBJECT (w
),
548 G_CALLBACK (cb_top10_type_changed
), state
);
553 w
= go_gtk_builder_get_widget (state
->gui
, "ok_button");
554 g_signal_connect (G_OBJECT (w
),
556 G_CALLBACK (cb_autofilter_ok
), state
);
557 w
= go_gtk_builder_get_widget (state
->gui
, "cancel_button");
558 g_signal_connect (G_OBJECT (w
),
560 G_CALLBACK (cb_autofilter_cancel
), state
);
562 /* a candidate for merging into attach guru */
563 gnm_init_help_button (
564 go_gtk_builder_get_widget (state
->gui
, "help_button"),
565 GNUMERIC_HELP_LINK_AUTOFILTER_TOP_TEN
);
567 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
),
569 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED
);
571 wbc_gtk_attach_guru (state
->wbcg
, state
->dialog
);
572 g_object_set_data_full (G_OBJECT (state
->dialog
),
573 "state", state
, (GDestroyNotify
)cb_autofilter_destroy
);
575 gnm_keyed_dialog (wbcg
, GTK_WINDOW (state
->dialog
),
577 gtk_widget_show (state
->dialog
);