PsppireDataStore: Implemenet get_flags function
[pspp] / src / ui / gui / psppire-data-store.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2006, 2008, 2009, 2010, 2011, 2012, 2013  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <gettext.h>
21 #define _(msgid) gettext (msgid)
22 #define N_(msgid) msgid
23
24 #include <data/datasheet.h>
25 #include <data/data-out.h>
26 #include <data/variable.h>
27
28 #include <ui/gui/psppire-marshal.h>
29
30 #include <pango/pango-context.h>
31
32 #include "psppire-data-store.h"
33 #include <libpspp/i18n.h>
34 #include "helper.h"
35
36 #include <data/dictionary.h>
37 #include <data/missing-values.h>
38 #include <data/value-labels.h>
39 #include <data/data-in.h>
40 #include <data/format.h>
41
42 #include <math/sort.h>
43
44 #include "xalloc.h"
45 #include "xmalloca.h"
46
47
48
49 static void psppire_data_store_init            (PsppireDataStore      *data_store);
50 static void psppire_data_store_class_init      (PsppireDataStoreClass *class);
51
52 static void psppire_data_store_finalize        (GObject           *object);
53 static void psppire_data_store_dispose        (GObject           *object);
54
55 static gboolean psppire_data_store_insert_case (PsppireDataStore *ds,
56                                                 struct ccase *cc,
57                                                 casenumber posn);
58
59
60 static gboolean psppire_data_store_data_in (PsppireDataStore *ds,
61                                             casenumber casenum, gint idx,
62                                             struct substring input,
63                                             const struct fmt_spec *fmt);
64
65 static GObjectClass *parent_class = NULL;
66
67
68 enum
69   {
70     ITEMS_CHANGED,
71     CASES_DELETED,
72     CASE_INSERTED,
73     CASE_CHANGED,
74     n_SIGNALS
75   };
76
77 static guint signals [n_SIGNALS];
78
79 static gint
80 __tree_model_iter_n_children (GtkTreeModel *tree_model,
81                              GtkTreeIter *iter)
82 {
83   PsppireDataStore *store  = PSPPIRE_DATA_STORE (tree_model);
84
85   gint n =  datasheet_get_n_rows (store->datasheet);
86
87   return n;
88 }
89
90 static GtkTreeModelFlags
91 __tree_model_get_flags (GtkTreeModel *model)
92 {
93   g_return_val_if_fail (PSPPIRE_IS_DATA_STORE (model), (GtkTreeModelFlags) 0);
94
95   return GTK_TREE_MODEL_LIST_ONLY;
96 }
97
98 static gint
99 __tree_model_get_n_columns (GtkTreeModel *tree_model)
100 {
101   PsppireDataStore *store  = PSPPIRE_DATA_STORE (tree_model);
102
103   return psppire_dict_get_value_cnt (store->dict);
104 }
105
106
107 static gboolean
108 __iter_nth_child (GtkTreeModel *tree_model,
109                   GtkTreeIter *iter,
110                   GtkTreeIter *parent,
111                   gint n)
112 {
113   PsppireDataStore *store  = PSPPIRE_DATA_STORE (tree_model);
114   
115   g_assert (parent == NULL);
116
117   g_return_val_if_fail (store, FALSE);
118   g_return_val_if_fail (store->datasheet, FALSE);
119
120   if (n >= datasheet_get_n_rows (store->datasheet))
121     {
122       iter->stamp = -1;
123       iter->user_data = NULL;
124       return FALSE;
125     }
126   
127   iter->user_data = n;
128   return TRUE;
129 }
130
131
132
133 static void
134 __get_value (GtkTreeModel *tree_model,
135              GtkTreeIter *iter,
136              gint column,
137              GValue *value)
138 {
139   PsppireDataStore *store  = PSPPIRE_DATA_STORE (tree_model);
140
141   g_value_init (value, G_TYPE_DOUBLE);
142
143   gint row = GPOINTER_TO_INT (iter->user_data);
144
145   struct ccase *cc = datasheet_get_row (store->datasheet, row);
146   
147   g_value_set_double (value, case_data_idx (cc, column)->f);
148   case_unref (cc);
149 }
150
151
152 static void
153 __tree_model_init (GtkTreeModelIface *iface)
154 {
155   iface->get_flags       = __tree_model_get_flags;
156   iface->get_n_columns   = __tree_model_get_n_columns ;
157   iface->get_column_type = NULL; 
158   iface->get_iter        = NULL; 
159   iface->iter_next       = NULL; 
160   iface->get_path        = NULL; 
161   iface->get_value       = __get_value;
162
163   iface->iter_children   = NULL; 
164   iface->iter_has_child  = NULL; 
165   iface->iter_n_children = __tree_model_iter_n_children;
166   iface->iter_nth_child  = __iter_nth_child;
167   iface->iter_parent     = NULL; 
168 }
169
170
171 GType
172 psppire_data_store_get_type (void)
173 {
174   static GType data_store_type = 0;
175
176   if (!data_store_type)
177     {
178       static const GTypeInfo data_store_info =
179       {
180         sizeof (PsppireDataStoreClass),
181         NULL,           /* base_init */
182         NULL,           /* base_finalize */
183         (GClassInitFunc) psppire_data_store_class_init,
184         NULL,           /* class_finalize */
185         NULL,           /* class_data */
186         sizeof (PsppireDataStore),
187         0,
188         (GInstanceInitFunc) psppire_data_store_init,
189       };
190
191       static const GInterfaceInfo tree_model_info = {
192         (GInterfaceInitFunc) __tree_model_init,
193         NULL,
194         NULL
195       };
196
197       data_store_type = g_type_register_static (G_TYPE_OBJECT,
198                                                 "PsppireDataStore",
199                                                 &data_store_info, 0);
200
201       g_type_add_interface_static (data_store_type, GTK_TYPE_TREE_MODEL,
202                                    &tree_model_info);
203     }
204
205   return data_store_type;
206 }
207
208
209 static void
210 psppire_data_store_class_init (PsppireDataStoreClass *class)
211 {
212   GObjectClass *object_class;
213
214   parent_class = g_type_class_peek_parent (class);
215   object_class = (GObjectClass*) class;
216
217   object_class->finalize = psppire_data_store_finalize;
218   object_class->dispose = psppire_data_store_dispose;
219
220     signals [ITEMS_CHANGED] =
221     g_signal_new ("changed",
222                   G_TYPE_FROM_CLASS (class),
223                   G_SIGNAL_RUN_FIRST,
224                   0,
225                   NULL, NULL,
226                   psppire_marshal_VOID__UINT_UINT_UINT,
227                   G_TYPE_NONE,
228                   3,
229                   G_TYPE_UINT,
230                   G_TYPE_UINT,
231                   G_TYPE_UINT);
232
233   signals [CASE_INSERTED] =
234     g_signal_new ("case-inserted",
235                   G_TYPE_FROM_CLASS (class),
236                   G_SIGNAL_RUN_FIRST,
237                   0,
238                   NULL, NULL,
239                   g_cclosure_marshal_VOID__INT,
240                   G_TYPE_NONE,
241                   1,
242                   G_TYPE_INT);
243
244
245   signals [CASE_CHANGED] =
246     g_signal_new ("case-changed",
247                   G_TYPE_FROM_CLASS (class),
248                   G_SIGNAL_RUN_FIRST,
249                   0,
250                   NULL, NULL,
251                   g_cclosure_marshal_VOID__INT,
252                   G_TYPE_NONE,
253                   1,
254                   G_TYPE_INT);
255
256   signals [CASES_DELETED] =
257     g_signal_new ("cases-deleted",
258                   G_TYPE_FROM_CLASS (class),
259                   G_SIGNAL_RUN_FIRST,
260                   0,
261                   NULL, NULL,
262                   psppire_marshal_VOID__INT_INT,
263                   G_TYPE_NONE,
264                   2,
265                   G_TYPE_INT,
266                   G_TYPE_INT);
267 }
268
269
270
271 static gboolean
272 psppire_data_store_insert_value (PsppireDataStore *ds,
273                                   gint width, gint where);
274
275 casenumber
276 psppire_data_store_get_case_count (const PsppireDataStore *store)
277 {
278   return datasheet_get_n_rows (store->datasheet);
279 }
280
281 size_t
282 psppire_data_store_get_value_count (const PsppireDataStore *store)
283 {
284   return psppire_dict_get_value_cnt (store->dict);
285 }
286
287 const struct caseproto *
288 psppire_data_store_get_proto (const PsppireDataStore *store)
289 {
290   return psppire_dict_get_proto (store->dict);
291 }
292
293 static void
294 psppire_data_store_init (PsppireDataStore *data_store)
295 {
296   data_store->dict = NULL;
297   data_store->datasheet = NULL;
298   data_store->dispose_has_run = FALSE;
299 }
300
301 /*
302    A callback which occurs after a variable has been deleted.
303  */
304 static void
305 delete_variable_callback (GObject *obj, const struct variable *var UNUSED,
306                           gint dict_index, gint case_index,
307                           gpointer data)
308 {
309   PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
310
311   g_return_if_fail (store->datasheet);
312
313   datasheet_delete_columns (store->datasheet, case_index, 1);
314   datasheet_insert_column (store->datasheet, NULL, -1, case_index);
315 }
316
317 struct resize_datum_aux
318   {
319     const struct dictionary *dict;
320     const struct variable *new_variable;
321     const struct variable *old_variable;
322   };
323
324 static void
325 resize_datum (const union value *old, union value *new, const void *aux_)
326 {
327   const struct resize_datum_aux *aux = aux_;
328   int new_width = var_get_width (aux->new_variable);
329   const char *enc = dict_get_encoding (aux->dict);
330   const struct fmt_spec *newfmt = var_get_print_format (aux->new_variable);
331   char *s = data_out (old, enc, var_get_print_format (aux->old_variable));
332   enum fmt_type type = (fmt_usable_for_input (newfmt->type)
333                         ? newfmt->type
334                         : FMT_DOLLAR);
335   free (data_in (ss_cstr (s), enc, type, new, new_width, enc));
336   free (s);
337 }
338
339 static void
340 variable_changed_callback (GObject *obj, gint var_num, guint what, const struct variable *oldvar,
341                            gpointer data)
342 {
343   PsppireDataStore *store  = PSPPIRE_DATA_STORE (data);
344   struct variable *variable = psppire_dict_get_variable (store->dict, var_num);
345
346   if (what & VAR_TRAIT_WIDTH)
347     {
348       int posn = var_get_case_index (variable);
349       struct resize_datum_aux aux;
350       aux.old_variable = oldvar;
351       aux.new_variable = variable;
352       aux.dict = store->dict->dict;
353       datasheet_resize_column (store->datasheet, posn, var_get_width (variable),
354                                resize_datum, &aux);
355     }
356 }
357
358 static void
359 insert_variable_callback (GObject *obj, gint var_num, gpointer data)
360 {
361   struct variable *variable;
362   PsppireDataStore *store;
363   gint posn;
364
365   g_return_if_fail (data);
366
367   store  = PSPPIRE_DATA_STORE (data);
368
369   variable = psppire_dict_get_variable (store->dict, var_num);
370   posn = var_get_case_index (variable);
371   psppire_data_store_insert_value (store, var_get_width (variable), posn);
372 }
373
374 /**
375  * psppire_data_store_new:
376  * @dict: The dictionary for this data_store.
377  *
378  *
379  * Return value: a new #PsppireDataStore
380  **/
381 PsppireDataStore *
382 psppire_data_store_new (PsppireDict *dict)
383 {
384   PsppireDataStore *retval;
385
386   retval = g_object_new (PSPPIRE_TYPE_DATA_STORE, NULL);
387
388   psppire_data_store_set_dictionary (retval, dict);
389
390   return retval;
391 }
392
393 void
394 psppire_data_store_set_reader (PsppireDataStore *ds,
395                                struct casereader *reader)
396 {
397   gint i;
398   gint old_n = 0;
399   if ( ds->datasheet)
400     {
401       old_n = datasheet_get_n_rows (ds->datasheet);
402       datasheet_destroy (ds->datasheet);
403     }
404
405   ds->datasheet = datasheet_create (reader);
406
407   gint new_n = datasheet_get_n_rows (ds->datasheet);
408   
409   if ( ds->dict )
410     for (i = 0 ; i < n_dict_signals; ++i )
411       {
412         if ( ds->dict_handler_id [i] > 0)
413           {
414             g_signal_handler_unblock (ds->dict,
415                                       ds->dict_handler_id[i]);
416           }
417       }
418
419   g_signal_emit (ds, signals[ITEMS_CHANGED], 0, 0, old_n, new_n);
420 }
421
422
423 /**
424  * psppire_data_store_replace_set_dictionary:
425  * @data_store: The variable store
426  * @dict: The dictionary to set
427  *
428  * If a dictionary is already associated with the data-store, then it will be
429  * destroyed.
430  **/
431 void
432 psppire_data_store_set_dictionary (PsppireDataStore *data_store, PsppireDict *dict)
433 {
434   int i;
435
436   /* Disconnect any existing handlers */
437   if ( data_store->dict )
438     for (i = 0 ; i < n_dict_signals; ++i )
439       {
440         g_signal_handler_disconnect (data_store->dict,
441                                      data_store->dict_handler_id[i]);
442       }
443
444   data_store->dict = dict;
445
446   if ( dict != NULL)
447     {
448
449       data_store->dict_handler_id [VARIABLE_INSERTED] =
450         g_signal_connect (dict, "variable-inserted",
451                           G_CALLBACK (insert_variable_callback),
452                           data_store);
453
454       data_store->dict_handler_id [VARIABLE_DELETED] =
455         g_signal_connect (dict, "variable-deleted",
456                           G_CALLBACK (delete_variable_callback),
457                           data_store);
458
459       data_store->dict_handler_id [VARIABLE_CHANGED] =
460         g_signal_connect (dict, "variable-changed",
461                           G_CALLBACK (variable_changed_callback),
462                           data_store);
463     }
464
465
466
467   /* The entire model has changed */
468
469   if ( data_store->dict )
470     for (i = 0 ; i < n_dict_signals; ++i )
471       {
472         if ( data_store->dict_handler_id [i] > 0)
473           {
474             g_signal_handler_block (data_store->dict,
475                                     data_store->dict_handler_id[i]);
476           }
477       }
478 }
479
480 static void
481 psppire_data_store_finalize (GObject *object)
482 {
483   PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
484
485   if (ds->datasheet)
486     {
487       datasheet_destroy (ds->datasheet);
488       ds->datasheet = NULL;
489     }
490
491   /* must chain up */
492   (* parent_class->finalize) (object);
493 }
494
495
496 static void
497 psppire_data_store_dispose (GObject *object)
498 {
499   PsppireDataStore *ds = PSPPIRE_DATA_STORE (object);
500
501   if (ds->dispose_has_run)
502     return;
503
504   psppire_data_store_set_dictionary (ds, NULL);
505
506   /* must chain up */
507   (* parent_class->dispose) (object);
508
509   ds->dispose_has_run = TRUE;
510 }
511
512
513
514 /* Insert a blank case before POSN */
515 gboolean
516 psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn)
517 {
518   gboolean result;
519   const struct caseproto *proto;
520   struct ccase *cc;
521   g_return_val_if_fail (ds, FALSE);
522
523   proto = datasheet_get_proto (ds->datasheet);
524   g_return_val_if_fail (caseproto_get_n_widths (proto) > 0, FALSE);
525   g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE);
526
527   cc = case_create (proto);
528   case_set_missing (cc);
529
530   result = psppire_data_store_insert_case (ds, cc, posn);
531
532   case_unref (cc);
533
534   return result;
535 }
536
537 gchar *
538 psppire_data_store_get_string (PsppireDataStore *store,
539                                glong row, const struct variable *var,
540                                bool use_value_label)
541 {
542   gchar *string;
543   union value v;
544   int width;
545
546   g_return_val_if_fail (store != NULL, NULL);
547   g_return_val_if_fail (store->datasheet != NULL, NULL);
548   g_return_val_if_fail (var != NULL, NULL);
549
550   if (row < 0 || row >= datasheet_get_n_rows (store->datasheet))
551     return NULL;
552
553   width = var_get_width (var);
554   value_init (&v, width);
555   datasheet_get_value (store->datasheet, row, var_get_case_index (var), &v);
556
557   string = NULL;
558   if (use_value_label)
559     {
560       const char *label = var_lookup_value_label (var, &v);
561       if (label != NULL)
562         string = g_strdup (label);
563     }
564   if (string == NULL)
565     string = value_to_text (v, var);
566
567   value_destroy (&v, width);
568
569   return string;
570 }
571
572
573 /* Attempts to update that part of the variable store which corresponds to VAR
574    within ROW with the value TEXT.
575
576    If USE_VALUE_LABEL is true, and TEXT is a value label for the column's
577    variable, then stores the value from that value label instead of the literal
578    TEXT.
579
580    Returns true if anything was updated, false otherwise.  */
581 gboolean
582 psppire_data_store_set_string (PsppireDataStore *store,
583                                const gchar *text,
584                                glong row, const struct variable *var,
585                                gboolean use_value_label)
586 {
587   gint case_index;
588   glong n_cases;
589   gboolean ok;
590
591   n_cases = psppire_data_store_get_case_count (store);
592   if (row > n_cases)
593     return FALSE;
594   if (row == n_cases)
595     psppire_data_store_insert_new_case (store, row);
596
597   case_index = var_get_case_index (var);
598   if (use_value_label)
599     {
600       const struct val_labs *vls = var_get_value_labels (var);
601       const union value *value = vls ? val_labs_find_value (vls, text) : NULL;
602       if (value)
603         ok = datasheet_put_value (store->datasheet, row, case_index, value);
604       else
605         ok = FALSE;
606     }
607   else
608     ok = psppire_data_store_data_in (store, row, case_index, ss_cstr (text),
609                                      var_get_print_format (var));
610
611   if (ok)
612     g_signal_emit (store, signals [CASE_CHANGED], 0, row);
613   return ok;
614 }
615
616
617
618 void
619 psppire_data_store_clear (PsppireDataStore *ds)
620 {
621   datasheet_destroy (ds->datasheet);
622   ds->datasheet = NULL;
623
624   psppire_dict_clear (ds->dict);
625
626   g_signal_emit (ds, signals [CASES_DELETED], 0, 0, -1);
627 }
628
629
630
631 /* Return a casereader made from this datastore */
632 struct casereader *
633 psppire_data_store_get_reader (PsppireDataStore *ds)
634 {
635   int i;
636   struct casereader *reader ;
637
638   if ( ds->dict )
639     for (i = 0 ; i < n_dict_signals; ++i )
640       {
641         g_signal_handler_block (ds->dict,
642                                 ds->dict_handler_id[i]);
643       }
644
645   reader = datasheet_make_reader (ds->datasheet);
646
647   /* We must not reference this again */
648   ds->datasheet = NULL;
649
650   return reader;
651 }
652
653
654
655 /* Column related funcs */
656
657
658 static const gchar null_var_name[]=N_("var");
659
660
661 \f
662
663
664 /* Returns the CASENUMth case, or a null pointer on failure.
665  */
666 struct ccase *
667 psppire_data_store_get_case (const PsppireDataStore *ds,
668                              casenumber casenum)
669 {
670   g_return_val_if_fail (ds, FALSE);
671   g_return_val_if_fail (ds->datasheet, FALSE);
672
673   return datasheet_get_row (ds->datasheet, casenum);
674 }
675
676
677 gboolean
678 psppire_data_store_delete_cases (PsppireDataStore *ds, casenumber first,
679                                  casenumber n_cases)
680 {
681   g_return_val_if_fail (ds, FALSE);
682   g_return_val_if_fail (ds->datasheet, FALSE);
683
684   g_return_val_if_fail (first + n_cases <=
685                         psppire_data_store_get_case_count (ds), FALSE);
686
687
688   datasheet_delete_rows (ds->datasheet, first, n_cases);
689
690   g_signal_emit (ds, signals [CASES_DELETED], 0, first, n_cases);
691
692   return TRUE;
693 }
694
695
696
697 /* Insert case CC into the case file before POSN */
698 static gboolean
699 psppire_data_store_insert_case (PsppireDataStore *ds,
700                                 struct ccase *cc,
701                                 casenumber posn)
702 {
703   bool result ;
704
705   g_return_val_if_fail (ds, FALSE);
706   g_return_val_if_fail (ds->datasheet, FALSE);
707
708   cc = case_ref (cc);
709   result = datasheet_insert_rows (ds->datasheet, posn, &cc, 1);
710
711   if ( result )
712     g_signal_emit (ds, signals [CASE_INSERTED], 0, posn);
713   else
714     g_warning ("Cannot insert case at position %ld\n", posn);
715
716   return result;
717 }
718
719
720 /* Set the value of VAR in case CASENUM to V.
721    V must be the correct width for IDX.
722    Returns true if successful, false on I/O error. */
723 gboolean
724 psppire_data_store_set_value (PsppireDataStore *ds, casenumber casenum,
725                               const struct variable *var, const union value *v)
726 {
727   glong n_cases;
728   bool ok;
729
730   g_return_val_if_fail (ds, FALSE);
731   g_return_val_if_fail (ds->datasheet, FALSE);
732
733   n_cases = psppire_data_store_get_case_count (ds);
734   if ( casenum > n_cases)
735     return FALSE;
736
737   if (casenum == n_cases)
738     psppire_data_store_insert_new_case (ds, casenum);
739
740   ok = datasheet_put_value (ds->datasheet, casenum, var_get_case_index (var),
741                             v);
742   if (ok)
743     g_signal_emit (ds, signals [CASE_CHANGED], 0, casenum);
744
745   return ok;
746 }
747
748
749
750
751 /* Set the IDXth value of case C using D_IN */
752 static gboolean
753 psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx,
754                             struct substring input, const struct fmt_spec *fmt)
755 {
756   union value value;
757   int width;
758   bool ok;
759
760   PsppireDict *dict;
761
762   g_return_val_if_fail (ds, FALSE);
763   g_return_val_if_fail (ds->datasheet, FALSE);
764
765   g_return_val_if_fail (idx < datasheet_get_n_columns (ds->datasheet), FALSE);
766
767   dict = ds->dict;
768
769   width = fmt_var_width (fmt);
770   g_return_val_if_fail (caseproto_get_width (
771                           datasheet_get_proto (ds->datasheet), idx) == width,
772                         FALSE);
773   value_init (&value, width);
774   ok = (datasheet_get_value (ds->datasheet, casenum, idx, &value)
775         && data_in_msg (input, UTF8, fmt->type, &value, width,
776                         dict_get_encoding (dict->dict))
777         && datasheet_put_value (ds->datasheet, casenum, idx, &value));
778   value_destroy (&value, width);
779
780   return ok;
781 }
782
783 /* Resize the cases in the casefile, by inserting a value of the
784    given WIDTH into every one of them at the position immediately
785    preceding WHERE.
786 */
787 static gboolean
788 psppire_data_store_insert_value (PsppireDataStore *ds,
789                                  gint width, gint where)
790 {
791   union value value;
792
793   g_return_val_if_fail (ds, FALSE);
794
795   g_assert (width >= 0);
796
797   if ( ! ds->datasheet )
798     ds->datasheet = datasheet_create (NULL);
799
800   value_init (&value, width);
801   value_set_missing (&value, width);
802
803   datasheet_insert_column (ds->datasheet, &value, width, where);
804   value_destroy (&value, width);
805
806   return TRUE;
807 }
808
809 gboolean
810 psppire_data_store_filtered (PsppireDataStore *ds,
811                              glong row)
812 {
813   union value val;
814
815   const struct dictionary *dict;
816   const struct variable *filter;
817
818   if ( row < 0 || row >= datasheet_get_n_rows (ds->datasheet))
819     return FALSE;
820
821   dict = ds->dict->dict;
822   g_return_val_if_fail (dict, FALSE);
823   filter = dict_get_filter (dict);
824   if ( ! filter)
825     return FALSE;
826
827   g_return_val_if_fail (var_is_numeric (filter), FALSE);
828   value_init (&val, 0);
829   if ( ! datasheet_get_value (ds->datasheet, row,
830                               var_get_case_index (filter),
831                               &val) )
832     return FALSE;
833
834   return (val.f == 0.0);
835 }