GUI: Find dialog: Compare only to the decimal places of the print format.
[pspp] / src / ui / gui / find-dialog.c
index 1a3a0a5c91afcfeb70067d2e29bf56887880eba6..2ff619a60a25c0c974dcc62092a97a7a40d9f540 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007, 2009, 2011, 2012  Free Software Foundation
+   Copyright (C) 2007, 2009, 2011, 2012, 2015, 2020  Free Software Foundation
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@ which match particular strings */
 #include <regex.h>
 #include <stdlib.h>
 #include <sys/types.h>
+#include <math.h>
 
 #include "data/data-in.h"
 #include "data/datasheet.h"
@@ -36,11 +37,11 @@ which match particular strings */
 #include "ui/gui/dict-display.h"
 #include "ui/gui/find-dialog.h"
 #include "ui/gui/helper.h"
-#include "ui/gui/psppire-data-sheet.h"
 #include "ui/gui/psppire-data-store.h"
 #include "ui/gui/psppire-data-window.h"
 #include "ui/gui/psppire-dialog.h"
 #include "ui/gui/psppire-selector.h"
+#include <ssw-sheet.h>
 
 #include "gl/xalloc.h"
 
@@ -100,28 +101,21 @@ refresh (GObject *obj, const struct find_dialog *fd)
 static void
 do_find (GObject *obj, const struct find_dialog *fd)
 {
-  PsppireDataSheet *data_sheet;
   casenumber x = -1;
   gint column = -1;
-  glong row;
-
-  data_sheet = psppire_data_editor_get_active_data_sheet (fd->de->data_editor);
-  row = psppire_data_sheet_get_selected_case (data_sheet);
-  if ( row < 0 )
-    row = 0;
+  glong row = -1;
 
   find_value (fd, row, &x, &column);
 
-
-  if ( x != -1)
+  if (x != -1)
     {
+      SswSheet *sheet = SSW_SHEET (fd->de->data_editor->data_sheet);
       gtk_notebook_set_current_page (GTK_NOTEBOOK (fd->de->data_editor),
                                     PSPPIRE_DATA_EDITOR_DATA_VIEW);
 
-      psppire_data_sheet_goto_case (data_sheet, x);
-      psppire_data_sheet_goto_variable (data_sheet, column);
+      ssw_sheet_scroll_to (sheet, column, x);
+      ssw_sheet_set_active_cell (sheet, column, x, NULL);
     }
-
 }
 
 /* Callback on the selector.
@@ -198,7 +192,7 @@ find_dialog (PsppireDataWindow *de)
   fd.xml = builder_new ("find.ui");
   fd.de = de;
 
-  find_button = gtk_button_new_from_stock  (GTK_STOCK_FIND);
+  find_button = gtk_button_new_with_label (_("Find"));
   gtk_widget_show (find_button);
 
   buttonbox = get_widget_assert (fd.xml, "find-buttonbox");
@@ -276,7 +270,7 @@ forward (casenumber *i, struct datasheet *data UNUSED)
 static void
 forward_wrap (casenumber *i, struct datasheet *data)
 {
-  if ( ++*i >=  datasheet_get_n_rows (data) ) *i = 0;
+  if (++*i >=  datasheet_get_n_rows (data)) *i = 0;
 }
 
 static void
@@ -289,7 +283,7 @@ backward (casenumber *i, struct datasheet *data UNUSED)
 static void
 backward_wrap (casenumber *i, struct datasheet *data)
 {
-  if ( --*i < 0 )
+  if (--*i < 0)
     *i = datasheet_get_n_rows (data) - 1;
 }
 
@@ -317,6 +311,9 @@ cp1c (casenumber current, struct datasheet *data)
 static casenumber
 cm1 (casenumber current, struct datasheet *data)
 {
+  if (current == -1)
+    return datasheet_get_n_rows (data);
+
   return current - 1;
 }
 
@@ -326,6 +323,9 @@ cm1c (casenumber current, struct datasheet *data)
 {
   casenumber next = current;
 
+  if (current == -1)
+    return datasheet_get_n_rows (data);
+
   backward_wrap (&next, data);
 
   return next;
@@ -341,6 +341,9 @@ last (casenumber current, struct datasheet *data)
 static casenumber
 minus1 (casenumber current, struct datasheet *data)
 {
+  if (current == -1)
+    return 0;
+
   return -1;
 }
 
@@ -385,16 +388,16 @@ get_iteration_params (const struct find_dialog *fd)
   gboolean reverse = gtk_toggle_button_get_active
     (GTK_TOGGLE_BUTTON (get_widget_assert (fd->xml, "find-backwards")));
 
-  if ( wrap )
+  if (wrap)
     {
-      if ( reverse )
+      if (reverse)
        return &ip[REVERSE_WRAP];
       else
        return &ip[FORWARD_WRAP];
     }
   else
     {
-      if ( reverse )
+      if (reverse)
        return &ip[REVERSE];
       else
        return &ip[FORWARD];
@@ -426,11 +429,13 @@ struct comparator
 };
 
 
-/* A comparator which operates on the unadulterated union values */
-struct value_comparator
+/* A comparator which operates on the numerical values,
+   rounded to the number of decimal places indicated by
+   the variable's format.  */
+struct numeric_comparator
 {
   struct comparator parent;
-  union value pattern;
+  double rounded_ref;
 };
 
 /* A comparator which matches string values or parts thereof */
@@ -452,8 +457,12 @@ static bool
 value_compare (const struct comparator *cmptr,
               const union value *v)
 {
-  const struct value_comparator *vc = (const struct value_comparator *) cmptr;
-  return 0 == value_compare_3way (v, &vc->pattern, var_get_width (cmptr->var));
+  const struct numeric_comparator *nc = (const struct numeric_comparator *) cmptr;
+  const struct fmt_spec *fs = var_get_print_format (cmptr->var);
+
+  double c = nearbyint (v->f * exp10 (fs->d));
+
+  return c == nc->rounded_ref;
 }
 
 
@@ -473,11 +482,11 @@ string_label_compare (const struct comparator *cmptr,
 
   width = strlen (text);
 
-  assert ( cmptr->flags & STR_CMP_LABELS);
+  assert (cmptr->flags & STR_CMP_LABELS);
 
   g_return_val_if_fail (width > 0, false);
 
-  if ( cmptr->flags & STR_CMP_SUBSTR)
+  if (cmptr->flags & STR_CMP_SUBSTR)
     return (NULL != g_strstr_len (text, width, ssc->pattern));
   else
     return (0 == strncmp (text, ssc->pattern, width));
@@ -495,11 +504,11 @@ string_value_compare (const struct comparator *cmptr,
 
   int width = var_get_width (cmptr->var);
   g_return_val_if_fail (width > 0, false);
-  assert ( ! (cmptr->flags & STR_CMP_LABELS));
+  assert (! (cmptr->flags & STR_CMP_LABELS));
 
   text = value_to_text (*val, cmptr->var);
 
-  if ( cmptr->flags & STR_CMP_SUBSTR)
+  if (cmptr->flags & STR_CMP_SUBSTR)
     found =  (NULL != g_strstr_len (text, width, ssc->pattern));
   else
     found = (0 == strncmp (text, ssc->pattern, width));
@@ -522,7 +531,7 @@ regexp_value_compare (const struct comparator *cmptr,
 
   int width = var_get_width (cmptr->var);
 
-  assert  ( ! (cmptr->flags & STR_CMP_LABELS) );
+  assert  (! (cmptr->flags & STR_CMP_LABELS));
 
   g_return_val_if_fail (width > 0, false);
 
@@ -549,7 +558,7 @@ regexp_label_compare (const struct comparator *cmptr,
 
   int width ;
 
-  assert ( cmptr->flags & STR_CMP_LABELS);
+  assert (cmptr->flags & STR_CMP_LABELS);
 
   text = var_lookup_value_label (cmptr->var, val);
   width = strlen (text);
@@ -570,27 +579,21 @@ regexp_destroy (struct comparator *cmptr)
   regfree (&rec->re);
 }
 
-static void
-cmptr_value_destroy (struct comparator *cmptr)
-{
-  struct value_comparator *vc
-    = UP_CAST (cmptr, struct value_comparator, parent);
-  value_destroy (&vc->pattern, var_get_width (cmptr->var));
-}
-
-
 static struct comparator *
-value_comparator_create (const struct variable *var, const char *target)
+numeric_comparator_create (const struct variable *var, const char *target)
 {
-  struct value_comparator *vc = xzalloc (sizeof (*vc));
-  struct comparator *cmptr = &vc->parent;
+  struct numeric_comparator *nc = xzalloc (sizeof (*nc));
+  struct comparator *cmptr = &nc->parent;
 
   cmptr->flags = 0;
   cmptr->var = var;
-  cmptr->compare  = value_compare ;
-  cmptr->destroy = cmptr_value_destroy;
+  cmptr->compare  = value_compare;
+  const struct fmt_spec *fs = var_get_write_format (var);
 
-  text_to_value (target, var, &vc->pattern);
+  union value val;
+  text_to_value (target, var, &val);
+  nc->rounded_ref = nearbyint (val.f * exp10 (fs->d));
+  value_destroy (&val, var_get_width (var));
 
   return cmptr;
 }
@@ -605,7 +608,7 @@ string_comparator_create (const struct variable *var, const char *target,
   cmptr->flags = flags;
   cmptr->var = var;
 
-  if ( flags & STR_CMP_LABELS)
+  if (flags & STR_CMP_LABELS)
     cmptr->compare = string_label_compare;
   else
     cmptr->compare = string_value_compare;
@@ -632,7 +635,7 @@ regexp_comparator_create (const struct variable *var, const char *target,
   cmptr->destroy  = regexp_destroy;
 
   code = regcomp (&rec->re, target, 0);
-  if ( code != 0 )
+  if (code != 0)
     {
       char *errbuf = NULL;
       size_t errbuf_size = regerror (code, &rec->re, errbuf,  0);
@@ -643,7 +646,7 @@ regexp_comparator_create (const struct variable *var, const char *target,
 
       msg (ME, _("Bad regular expression: %s"), errbuf);
 
-      free ( cmptr);
+      free (cmptr);
       free (errbuf);
       return NULL;
     }
@@ -664,10 +667,10 @@ comparator_compare (const struct comparator *cmptr,
 static void
 comparator_destroy (struct comparator *cmptr)
 {
-  if ( ! cmptr )
+  if (! cmptr)
     return ;
 
-  if ( cmptr->destroy )
+  if (cmptr->destroy)
     cmptr->destroy (cmptr);
 
   free (cmptr);
@@ -678,13 +681,13 @@ static struct comparator *
 comparator_factory (const struct variable *var, const char *str,
                    enum string_cmp_flags flags)
 {
-  if ( flags & STR_CMP_REGEXP )
+  if (flags & STR_CMP_REGEXP)
     return regexp_comparator_create (var, str, flags);
 
-  if ( flags & (STR_CMP_SUBSTR | STR_CMP_LABELS) )
+  if (flags & (STR_CMP_SUBSTR | STR_CMP_LABELS))
     return string_comparator_create (var, str, flags);
 
-  return value_comparator_create (var, str);
+  return numeric_comparator_create (var, str);
 }
 
 
@@ -702,10 +705,9 @@ find_value (const struct find_dialog *fd, casenumber current_row,
   const char *target_string = gtk_entry_get_text (GTK_ENTRY (fd->value_entry));
 
   enum string_cmp_flags flags = 0;
-  g_assert (current_row >= 0);
 
   var = dict_lookup_var (fd->dict->dict, var_name);
-  if ( ! var )
+  if (! var)
     return ;
 
   width = var_get_width (var);
@@ -713,15 +715,15 @@ find_value (const struct find_dialog *fd, casenumber current_row,
   *column = var_get_dict_index (var);
   *row = -1;
 
-  if ( gtk_toggle_button_get_active
+  if (gtk_toggle_button_get_active
        (GTK_TOGGLE_BUTTON (fd->match_substring_checkbox)))
     flags |= STR_CMP_SUBSTR;
 
-  if ( gtk_toggle_button_get_active
+  if (gtk_toggle_button_get_active
        (GTK_TOGGLE_BUTTON (fd->match_regexp_checkbox)))
     flags |= STR_CMP_REGEXP;
 
-  if ( gtk_toggle_button_get_active
+  if (gtk_toggle_button_get_active
        (GTK_TOGGLE_BUTTON (fd->value_labels_checkbox)))
     flags |= STR_CMP_LABELS;
 
@@ -733,7 +735,7 @@ find_value (const struct find_dialog *fd, casenumber current_row,
       comparator_factory (var, target_string, flags);
 
     value_init (&val, width);
-    if ( ! cmptr)
+    if (! cmptr)
       goto finish;
 
     for (i = ip->start (current_row, fd->data);
@@ -742,7 +744,7 @@ find_value (const struct find_dialog *fd, casenumber current_row,
       {
        datasheet_get_value (fd->data, i, var_get_case_index (var), &val);
 
-       if ( comparator_compare (cmptr, &val))
+       if (comparator_compare (cmptr, &val))
          {
            *row = i;
            break;