help: added help page info to variable sheet dialogs
[pspp] / src / ui / gui / missing-val-dialog.c
index 030d0786709a9a1a5be9d434882d1941668856b0..09e17c1aaff4046989f0866e7529ab5bb6e45dfb 100644 (file)
@@ -1,5 +1,6 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2005, 2006, 2009, 2011, 2012  Free Software Foundation
+   Copyright (C) 2005, 2006, 2009, 2011, 2012, 2015, 2016,
+   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
@@ -147,12 +148,11 @@ psppire_missing_val_dialog_new (const struct variable *var)
 {
   return PSPPIRE_MISSING_VAL_DIALOG (
     g_object_new (PSPPIRE_TYPE_MISSING_VAL_DIALOG,
-                  "orientation", PSPPIRE_VERTICAL,
                   "variable", var,
                   NULL));
 }
 
-void
+gint
 psppire_missing_val_dialog_run (GtkWindow *parent_window,
                                 const struct variable *var,
                                 struct missing_values *mv)
@@ -164,12 +164,14 @@ psppire_missing_val_dialog_run (GtkWindow *parent_window,
   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
   gtk_widget_show (GTK_WIDGET (dialog));
 
-  if (psppire_dialog_run (PSPPIRE_DIALOG (dialog)) == GTK_RESPONSE_OK)
+  gint result = psppire_dialog_run (PSPPIRE_DIALOG (dialog));
+  if (result == GTK_RESPONSE_OK)
     mv_copy (mv, psppire_missing_val_dialog_get_missing_values (dialog));
   else
     mv_copy (mv, var_get_missing_values (var));
 
   gtk_widget_destroy (GTK_WIDGET (dialog));
+  return result;
 }
 
 
@@ -177,39 +179,52 @@ psppire_missing_val_dialog_run (GtkWindow *parent_window,
 static void
 err_dialog (const gchar *msg, GtkWindow *window)
 {
-  GtkWidget *hbox ;
-  GtkWidget *label = gtk_label_new (msg);
-
   GtkWidget *dialog =
-    gtk_dialog_new_with_buttons ("PSPP",
-                                window,
-                                GTK_DIALOG_MODAL |
-                                GTK_DIALOG_DESTROY_WITH_PARENT, 
-                                GTK_STOCK_OK,
-                                GTK_RESPONSE_ACCEPT,
-                                NULL);
-
-
-  GtkWidget *icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR,
-                                            GTK_ICON_SIZE_DIALOG);
-
-  g_signal_connect_swapped (dialog,
-                           "response",
-                           G_CALLBACK (gtk_widget_destroy),
-                           dialog);
-
-  hbox = gtk_hbox_new (FALSE, 10);
-
-  gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
-                    hbox);
+    gtk_message_dialog_new (window,
+                           GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                           GTK_MESSAGE_ERROR,
+                           GTK_BUTTONS_CLOSE,
+                           "%s",msg);
+
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+}
 
-  gtk_box_pack_start (GTK_BOX (hbox), icon, TRUE, FALSE, 10);
-  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 10);
+/* Interpret text, display error dialog
+   If parsing is o.k., the value is initialized and it is the responsibility of
+   the caller to destroy the variable. */
+static gboolean
+try_missing_value(const PsppireMissingValDialog *dialog, const gchar *text, union value *vp)
+{
+  const int var_width = fmt_var_width (&dialog->format);
+  char *error_txt = NULL;
 
-  gtk_widget_show_all (dialog);
+  value_init(vp, var_width);
+  error_txt = data_in (ss_cstr(text), "UTF-8", dialog->format.type,
+                      vp, var_width, dialog->encoding);
+  if (error_txt)
+    {
+      err_dialog (error_txt, GTK_WINDOW (dialog));
+      free (error_txt);
+      goto error;
+    }
+  else
+    {
+      if (mv_is_acceptable (vp, var_width))
+       return TRUE;
+      else
+       {
+         err_dialog (_("The maximum length of a missing value"
+                       " for a string variable is 8 in UTF-8."),
+                     GTK_WINDOW (dialog));
+         goto error;
+       }
+    }
+ error:
+  value_destroy (vp, var_width);
+  return FALSE;
 }
 
-
 /* Acceptability predicate for PsppireMissingValDialog.
 
    This function is also the only place that dialog->mvl gets updated. */
@@ -217,100 +232,98 @@ static gboolean
 missing_val_dialog_acceptable (gpointer data)
 {
   PsppireMissingValDialog *dialog = data;
+  int var_width = fmt_var_width (&dialog->format);
 
-  if ( gtk_toggle_button_get_active (dialog->button_discrete))
+  if (gtk_toggle_button_get_active (dialog->button_discrete))
     {
       gint nvals = 0;
-      gint badvals = 0;
       gint i;
+
       mv_clear(&dialog->mvl);
-      for(i = 0 ; i < 3 ; ++i )
+      for(i = 0 ; i < 3 ; ++i)
        {
          gchar *text =
            g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->mv[i])));
 
          union value v;
-         if ( !text || strlen (g_strstrip (text)) == 0 )
+         if (!text || strlen (g_strstrip (text)) == 0)
            {
              g_free (text);
              continue;
            }
 
-         if ( text_to_value__ (text, &dialog->format, dialog->encoding, &v))
+         if (!try_missing_value (dialog, text, &v))
            {
-             nvals++;
-             mv_add_value (&dialog->mvl, &v);
+             g_free (text);
+             gtk_widget_grab_focus (dialog->mv[i]);
+             return FALSE;
            }
-         else
-             badvals++;
+         mv_add_value (&dialog->mvl, &v);
+         nvals++;
          g_free (text);
-         value_destroy (&v, fmt_var_width (&dialog->format));
+         value_destroy (&v, var_width);
        }
-      if ( nvals == 0 || badvals > 0 )
+      if (nvals == 0)
        {
-         err_dialog (_("Incorrect value for variable type"),
-                      GTK_WINDOW (dialog));
+         err_dialog (_("At least one value must be specified"),
+                     GTK_WINDOW (dialog));
+         gtk_widget_grab_focus (dialog->mv[0]);
          return FALSE;
        }
     }
 
   if (gtk_toggle_button_get_active (dialog->button_range))
     {
-      gchar *discrete_text ;
-
+      gchar *discrete_text;
       union value low_val ;
       union value high_val;
       const gchar *low_text = gtk_entry_get_text (GTK_ENTRY (dialog->low));
       const gchar *high_text = gtk_entry_get_text (GTK_ENTRY (dialog->high));
-      gboolean low_ok;
-      gboolean high_ok;
-      gboolean ok;
-
-      low_ok = text_to_value__ (low_text, &dialog->format, dialog->encoding,
-                                &low_val) != NULL;
-      high_ok = text_to_value__ (high_text, &dialog->format, dialog->encoding,
-                                 &high_val) != NULL;
-      ok = low_ok && high_ok && low_val.f <= high_val.f;
-      if (!ok)
-        {
-          err_dialog (_("Incorrect range specification"), GTK_WINDOW (dialog));
-          if (low_ok)
-            value_destroy (&low_val, fmt_var_width (&dialog->format));
-          if (high_ok)
-            value_destroy (&high_val, fmt_var_width (&dialog->format));
-          return FALSE;
-        }
-
-      discrete_text =
-       g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->discrete)));
 
+      assert (var_width == 0); /* Ranges are only for numeric variables */
+
+      if (!try_missing_value(dialog, low_text, &low_val))
+       {
+         gtk_widget_grab_focus (dialog->low);
+         return FALSE;
+       }
+      if (!try_missing_value (dialog, high_text, &high_val))
+       {
+         gtk_widget_grab_focus (dialog->high);
+         value_destroy (&low_val, var_width);
+         return FALSE;
+       }
+      if (low_val.f > high_val.f)
+       {
+         err_dialog (_("Incorrect range specification"),
+                     GTK_WINDOW (dialog));
+         value_destroy (&low_val, var_width);
+         value_destroy (&high_val, var_width);
+         gtk_widget_grab_focus (dialog->low);
+         return FALSE;
+       }
       mv_clear (&dialog->mvl);
       mv_add_range (&dialog->mvl, low_val.f, high_val.f);
+      value_destroy (&low_val, var_width);
+      value_destroy (&high_val, var_width);
 
-      value_destroy (&low_val, fmt_var_width (&dialog->format));
-      value_destroy (&high_val, fmt_var_width (&dialog->format));
+      discrete_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->discrete)));
 
-      if ( discrete_text && strlen (g_strstrip (discrete_text)) > 0 )
+      if (discrete_text && strlen (g_strstrip (discrete_text)) > 0)
        {
          union value discrete_val;
-         if ( !text_to_value__ (discrete_text,
-                                 &dialog->format,
-                                 dialog->encoding,
-                                 &discrete_val))
+         if (!try_missing_value (dialog, discrete_text, &discrete_val))
            {
-             err_dialog (_("Incorrect value for variable type"),
-                        GTK_WINDOW (dialog) );
              g_free (discrete_text);
-             value_destroy (&discrete_val, fmt_var_width (&dialog->format));
+             gtk_widget_grab_focus (dialog->discrete);
              return FALSE;
            }
          mv_add_value (&dialog->mvl, &discrete_val);
-         value_destroy (&discrete_val, fmt_var_width (&dialog->format));
+         value_destroy (&discrete_val, var_width);
        }
       g_free (discrete_text);
     }
 
-
   if (gtk_toggle_button_get_active (dialog->button_none))
     mv_clear (&dialog->mvl);
 
@@ -325,7 +338,7 @@ discrete (GtkToggleButton *button, gpointer data)
   gint i;
   PsppireMissingValDialog *dialog = data;
 
-  for (i = 0 ; i < 3 ; ++i )
+  for (i = 0 ; i < 3 ; ++i)
     {
       gtk_widget_set_sensitive (dialog->mv[i],
                               gtk_toggle_button_get_active (button));
@@ -362,7 +375,9 @@ psppire_missing_val_dialog_constructor (GType                  type,
     type, n_properties, properties);
   dialog = PSPPIRE_MISSING_VAL_DIALOG (obj);
 
-  content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog)->box);
+  g_object_set (dialog, "help_page", "Missing-Observations", NULL);
+
+  content_area = GTK_CONTAINER (PSPPIRE_DIALOG (dialog));
   xml = builder_new ("missing-val-dialog.ui");
   gtk_container_add (GTK_CONTAINER (content_area),
                      get_widget_assert (xml, "missing-values-dialog"));
@@ -412,7 +427,11 @@ psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog,
 
   if (var != NULL)
     {
-      mv_copy (&dialog->mvl, var_get_missing_values (var));
+      const struct missing_values *vmv = var_get_missing_values (var);
+      if (mv_is_empty(vmv))
+       mv_init (&dialog->mvl, var_get_width(var));
+      else
+       mv_copy (&dialog->mvl, vmv);
       dialog->encoding = g_strdup (var_get_encoding (var));
       dialog->format = *var_get_print_format (var);
     }
@@ -438,13 +457,13 @@ psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog,
   if (var == NULL)
     return;
 
-  for (i = 0 ; i < 3 ; ++i )
+  for (i = 0 ; i < 3 ; ++i)
     {
       gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), "");
       gtk_widget_set_sensitive (dialog->mv[i], FALSE);
     }
 
-  if ( mv_has_range (&dialog->mvl))
+  if (mv_has_range (&dialog->mvl))
     {
       union value low, high;
       gchar *low_text;
@@ -460,7 +479,7 @@ psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog,
       g_free (low_text);
       g_free (high_text);
 
-      if ( mv_has_value (&dialog->mvl))
+      if (mv_has_value (&dialog->mvl))
        {
          gchar *text;
          text = value_to_text__ (*mv_get_value (&dialog->mvl, 0),
@@ -475,13 +494,13 @@ psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog,
       gtk_widget_set_sensitive (dialog->discrete, TRUE);
 
     }
-  else if ( mv_has_value (&dialog->mvl))
+  else if (mv_has_value (&dialog->mvl))
     {
       const int n = mv_n_values (&dialog->mvl);
 
-      for (i = 0 ; i < 3 ; ++i )
+      for (i = 0 ; i < 3 ; ++i)
        {
-         if ( i < n)
+         if (i < n)
            {
              gchar *text ;
 
@@ -494,7 +513,7 @@ psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog,
        }
       gtk_toggle_button_set_active (dialog->button_discrete, TRUE);
     }
-  else if ( mv_is_empty (&dialog->mvl))
+  else if (mv_is_empty (&dialog->mvl))
     {
       gtk_toggle_button_set_active (dialog->button_none, TRUE);
     }