3 * gnm-filter-combo.c: the autofilter combo box for the goffice canvas
5 * Copyright (C) 2006 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 #include <gnumeric-config.h>
24 #include <widgets/gnm-filter-combo-view.h>
25 #include <widgets/gnm-cell-combo-view-impl.h>
28 #include <sheet-filter.h>
29 #include <sheet-filter-combo.h>
30 #include <gnm-format.h>
35 #include <sheet-object-impl.h>
38 #include <style-color.h>
39 #include <sheet-control-gui.h>
40 #include <dialogs/dialogs.h>
41 #include <wbc-gtk-impl.h>
44 #include <goffice/goffice.h>
45 #include <gsf/gsf-impl-utils.h>
46 #include <glib/gi18n-lib.h>
50 fcombo_activate (SheetObject
*so
, GtkTreeView
*list
, WBCGtk
*wbcg
,
51 G_GNUC_UNUSED gboolean button
)
53 GnmFilterCombo
*fcombo
= GNM_FILTER_COMBO (so
);
56 if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (list
), NULL
, &iter
)) {
57 GnmFilterCondition
*cond
= NULL
;
58 gboolean set_condition
= TRUE
;
62 gtk_tree_model_get (gtk_tree_view_get_model (list
), &iter
,
66 field_num
= gnm_filter_combo_index (fcombo
);
69 cond
= gnm_filter_condition_new_single (
70 GNM_FILTER_OP_EQUAL
, v
);
72 case 1: /* unfilter */
76 set_condition
= FALSE
;
77 dialog_auto_filter (wbcg
, fcombo
->filter
, field_num
,
81 cond
= gnm_filter_condition_new_single (
82 GNM_FILTER_OP_BLANKS
, NULL
);
85 cond
= gnm_filter_condition_new_single (
86 GNM_FILTER_OP_NON_BLANKS
, NULL
);
89 set_condition
= FALSE
;
90 dialog_auto_filter (wbcg
, fcombo
->filter
, field_num
,
94 set_condition
= FALSE
;
95 g_warning ("Unknown type %d", type
);
99 cmd_autofilter_set_condition
101 fcombo
->filter
, field_num
, cond
);
109 GODateConventions
const *date_conv
;
114 cb_collect_content (GnmCellIter
const *iter
, UniqueCollection
*uc
)
116 GnmCell
const *cell
= (iter
->pp
.sheet
== uc
->src_sheet
) ? iter
->cell
117 : sheet_cell_get (uc
->src_sheet
,
118 iter
->pp
.eval
.col
, iter
->pp
.eval
.row
);
120 if (gnm_cell_is_blank (cell
))
121 uc
->has_blank
= TRUE
;
123 GOFormat
const *fmt
= gnm_cell_get_format (cell
);
124 GnmValue
const *v
= cell
->value
;
125 g_hash_table_replace (uc
->hash
,
127 format_value (fmt
, v
, -1, uc
->date_conv
));
134 cb_hash_domain (GnmValue
*key
, gpointer value
, gpointer accum
)
136 g_ptr_array_add (accum
, key
);
139 /* Distinguish between the same value with different formats */
141 formatted_value_equal (GnmValue
const *a
, GnmValue
const *b
)
143 return value_equal (a
, b
) && (VALUE_FMT(a
) == VALUE_FMT(b
));
147 fcombo_create_list (SheetObject
*so
,
148 GtkTreePath
**clip
, GtkTreePath
**select
, gboolean
*make_buttons
)
150 GnmFilterCombo
*fcombo
= GNM_FILTER_COMBO (so
);
151 GnmFilter
const *filter
= fcombo
->filter
;
152 GnmRange r
= filter
->r
;
153 Sheet
*filtered_sheet
;
158 GPtrArray
*sorted
= g_ptr_array_new ();
159 unsigned i
, field_num
= gnm_filter_combo_index (fcombo
);
160 gboolean is_custom
= FALSE
;
162 GnmValue
const *cur_val
= NULL
;
164 model
= gtk_list_store_new (4,
165 G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_INT
, gnm_value_get_type ());
167 gtk_list_store_append (model
, &iter
);
168 gtk_list_store_set (model
, &iter
, 0, _("(All)"), 1, NULL
, 2, 1, -1);
169 if (fcombo
->cond
== NULL
|| fcombo
->cond
->op
[0] == GNM_FILTER_UNUSED
)
170 *select
= gtk_tree_model_get_path (GTK_TREE_MODEL (model
), &iter
);
172 gtk_list_store_append (model
, &iter
);
173 gtk_list_store_set (model
, &iter
, 0, _("(Top 10...)"), 1, NULL
, 2, 10,-1);
174 if (fcombo
->cond
!= NULL
&&
175 (GNM_FILTER_OP_TYPE_MASK
& fcombo
->cond
->op
[0]) == GNM_FILTER_OP_TOP_N
)
176 *select
= gtk_tree_model_get_path (GTK_TREE_MODEL (model
), &iter
);
178 /* default to this we can easily revamp later */
179 gtk_list_store_append (model
, &iter
);
180 gtk_list_store_set (model
, &iter
, 0, _("(Custom...)"), 1, NULL
, 2, 2, -1);
181 if (*select
== NULL
) {
183 *select
= gtk_tree_model_get_path (GTK_TREE_MODEL (model
), &iter
);
187 /* r.end.row = XL actually extend to the first non-empty element in the list */
188 r
.end
.col
= r
.start
.col
+= field_num
;
189 uc
.has_blank
= FALSE
;
190 uc
.hash
= g_hash_table_new_full ((GHashFunc
)value_hash
, (GEqualFunc
)formatted_value_equal
,
191 (GDestroyNotify
)value_release
, (GDestroyNotify
)g_free
);
192 uc
.src_sheet
= filter
->sheet
;
193 uc
.date_conv
= sheet_date_conv (uc
.src_sheet
);
195 /* We do not want to show items that are filtered by _other_ fields.
196 * The cleanest way to do that is to create a temporary sheet, apply
197 * all of the other conditions to it and use that as the source of visibility. */
198 if (filter
->fields
->len
> 1) {
199 Workbook
*wb
= uc
.src_sheet
->workbook
;
200 char *name
= workbook_sheet_get_free_name (wb
, "DummyFilterPopulate", FALSE
, FALSE
);
201 filtered_sheet
= sheet_new (wb
, name
,
202 gnm_sheet_get_max_cols (uc
.src_sheet
),
203 gnm_sheet_get_max_rows (uc
.src_sheet
));
205 for (i
= 0 ; i
< filter
->fields
->len
; i
++)
207 gnm_filter_combo_apply (g_ptr_array_index (filter
->fields
, i
),
209 sheet_foreach_cell_in_range (filtered_sheet
,
210 CELL_ITER_IGNORE_HIDDEN
,
212 (CellIterFunc
)&cb_collect_content
, &uc
);
213 g_object_unref (filtered_sheet
);
215 sheet_foreach_cell_in_range (filter
->sheet
, CELL_ITER_ALL
,
217 (CellIterFunc
)&cb_collect_content
, &uc
);
219 g_hash_table_foreach (uc
.hash
, (GHFunc
)cb_hash_domain
, sorted
);
220 g_ptr_array_sort (sorted
, value_cmp
);
222 if (fcombo
->cond
!= NULL
&&
223 fcombo
->cond
->op
[0] == GNM_FILTER_OP_EQUAL
&&
224 fcombo
->cond
->op
[1] == GNM_FILTER_UNUSED
) {
225 cur_val
= fcombo
->cond
->value
[0];
228 for (i
= 0; i
< sorted
->len
; i
++) {
230 unsigned const max
= 50;
231 char const *str
= g_hash_table_lookup (uc
.hash
,
232 (v
= g_ptr_array_index (sorted
, i
)));
233 gsize len
= g_utf8_strlen (str
, -1);
236 label
= g_strdup (str
);
237 strcpy (g_utf8_offset_to_pointer (label
, max
), "...");
240 gtk_list_store_append (model
, &iter
);
241 gtk_list_store_set (model
, &iter
,
242 0, label
? label
: str
, /* Menu text */
243 1, str
, /* Actual string selected on. */
249 *clip
= gtk_tree_model_get_path (GTK_TREE_MODEL (model
), &iter
);
250 if (cur_val
!= NULL
&& v
!= NULL
&& value_equal (cur_val
, v
)) {
251 gtk_tree_path_free (*select
);
252 *select
= gtk_tree_model_get_path (GTK_TREE_MODEL (model
), &iter
);
257 gtk_list_store_append (model
, &iter
);
258 gtk_list_store_set (model
, &iter
, 0, _("(Blanks...)"), 1, NULL
, 2, 3, -1);
259 if (fcombo
->cond
!= NULL
&&
260 fcombo
->cond
->op
[0] == GNM_FILTER_OP_BLANKS
)
261 *select
= gtk_tree_model_get_path (GTK_TREE_MODEL (model
), &iter
);
263 gtk_list_store_append (model
, &iter
);
264 gtk_list_store_set (model
, &iter
, 0, _("(Non Blanks...)"), 1, NULL
, 2, 4, -1);
265 if (fcombo
->cond
!= NULL
&&
266 fcombo
->cond
->op
[0] == GNM_FILTER_OP_NON_BLANKS
)
267 *select
= gtk_tree_model_get_path (GTK_TREE_MODEL (model
), &iter
);
268 } else if (is_custom
&& fcombo
->cond
!= NULL
&&
269 (GNM_FILTER_OP_TYPE_MASK
& fcombo
->cond
->op
[0]) == GNM_FILTER_OP_BLANKS
) {
270 gtk_tree_path_free (*select
);
274 g_hash_table_destroy (uc
.hash
);
275 g_ptr_array_free (sorted
, TRUE
);
277 list
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (model
));
278 g_object_unref (model
);
279 gtk_tree_view_append_column (GTK_TREE_VIEW (list
),
280 gtk_tree_view_column_new_with_attributes ("ID",
281 gtk_cell_renderer_text_new (), "text", 0,
287 fcombo_arrow_format (GnmFilterCombo
*fcombo
, GtkWidget
*arrow
)
289 if (gtk_widget_get_parent (arrow
)) {
291 if (NULL
!= fcombo
->cond
) {
294 gtk_widget_set_tooltip_text (gtk_widget_get_parent (arrow
), desc
);
299 gtk_arrow_set (GTK_ARROW (arrow
),
300 fcombo
->cond
!= NULL
? GTK_ARROW_RIGHT
: GTK_ARROW_DOWN
,
303 gtk_widget_set_state_flags (arrow
, GTK_STATE_FLAG_ACTIVE
, FALSE
);
305 gtk_widget_unset_state_flags (arrow
, GTK_STATE_FLAG_ACTIVE
);
309 fcombo_create_arrow (SheetObject
*so
)
311 GnmFilterCombo
*fcombo
= GNM_FILTER_COMBO (so
);
312 GtkWidget
*arrow
= gtk_arrow_new (GTK_ARROW_DOWN
, GTK_SHADOW_IN
);
313 gtk_style_context_add_class (gtk_widget_get_style_context (arrow
),
315 fcombo_arrow_format (fcombo
, arrow
);
316 g_signal_connect_object (G_OBJECT (so
),
318 G_CALLBACK (fcombo_arrow_format
), arrow
, 0);
322 /*******************************************************************************/
325 * We do not honour all of the anchor flags. All that is used is the far corner. */
327 filter_view_set_bounds (SheetObjectView
*sov
, double const *coords
, gboolean visible
)
329 GocGroup
*view
= GOC_GROUP (sov
);
332 double scale
= goc_canvas_get_pixels_per_unit (GOC_ITEM (view
)->canvas
);
333 double h
= (coords
[3] - coords
[1]) + 1.;
334 if (h
> 20.) /* clip vertically */
337 goc_item_set (GOC_ITEM (view
->children
->data
),
338 /* put it inside the cell */
339 "x", ((coords
[2] >= 0.) ? (coords
[2] / scale
- h
+ 1) : coords
[0] / scale
),
340 "y", coords
[3] / scale
- h
+ 1.,
341 "width", h
, /* force a square, use h for width too */
345 goc_item_show (GOC_ITEM (view
));
347 goc_item_hide (GOC_ITEM (view
));
351 gnm_filter_view_class_init (GnmCComboViewClass
*ccombo_class
)
353 SheetObjectViewClass
*sov_class
= (SheetObjectViewClass
*) ccombo_class
;
354 ccombo_class
->create_list
= fcombo_create_list
;
355 ccombo_class
->create_arrow
= fcombo_create_arrow
;
356 ccombo_class
->activate
= fcombo_activate
;
357 sov_class
->set_bounds
= filter_view_set_bounds
;
360 /****************************************************************************/
362 typedef GnmCComboView GnmFilterComboView
;
363 typedef GnmCComboViewClass GnmFilterComboViewClass
;
364 GSF_CLASS (GnmFilterComboView
, gnm_filter_combo_view
,
365 gnm_filter_view_class_init
, NULL
,
366 GNM_CCOMBO_VIEW_TYPE
)