X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fgui%2Fmissing-val-dialog.c;h=83ec504edde789941812d75c392df4ec445c8f9e;hb=17ca35e4ca8abf4ba0c0ae82920b985fa17648cf;hp=3e42036cb17260bf3c38c2c04d4ffa9cbd382c17;hpb=d46430e7458fcd65dfdf7928f52f2d06fa60127d;p=pspp diff --git a/src/ui/gui/missing-val-dialog.c b/src/ui/gui/missing-val-dialog.c index 3e42036cb1..83ec504edd 100644 --- a/src/ui/gui/missing-val-dialog.c +++ b/src/ui/gui/missing-val-dialog.c @@ -1,5 +1,5 @@ /* 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 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 @@ -18,10 +18,8 @@ used for input of the missing values in the variable sheet */ #include -#include -#define _(msgid) gettext (msgid) -#define N_(msgid) msgid +#include "ui/gui/missing-val-dialog.h" #include "builder-wrapper.h" #include "helper.h" @@ -31,61 +29,215 @@ #include #include - #include #include +#include +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + +static GObject *psppire_missing_val_dialog_constructor ( + GType type, guint, GObjectConstructParam *); +static void psppire_missing_val_dialog_finalize (GObject *); + +G_DEFINE_TYPE (PsppireMissingValDialog, + psppire_missing_val_dialog, + PSPPIRE_TYPE_DIALOG); +enum + { + PROP_0, + PROP_VARIABLE, + PROP_MISSING_VALUES + }; -/* A simple (sub) dialog box for displaying user input errors */ static void -err_dialog (const gchar *msg, GtkWindow *window) +psppire_missing_val_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - GtkWidget *hbox ; - GtkWidget *label = gtk_label_new (msg); + PsppireMissingValDialog *obj = PSPPIRE_MISSING_VAL_DIALOG (object); - GtkWidget *dialog = - gtk_dialog_new_with_buttons ("PSPP", - window, - GTK_DIALOG_MODAL | - GTK_DIALOG_DESTROY_WITH_PARENT | - GTK_DIALOG_NO_SEPARATOR, - GTK_STOCK_OK, - GTK_RESPONSE_ACCEPT, - NULL); + switch (prop_id) + { + case PROP_VARIABLE: + psppire_missing_val_dialog_set_variable (obj, + g_value_get_pointer (value)); + break; + case PROP_MISSING_VALUES: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} +static void +psppire_missing_val_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PsppireMissingValDialog *obj = PSPPIRE_MISSING_VAL_DIALOG (object); - GtkWidget *icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_ERROR, - GTK_ICON_SIZE_DIALOG); + switch (prop_id) + { + case PROP_MISSING_VALUES: + g_value_set_pointer (value, &obj->mvl); + break; + case PROP_VARIABLE: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} - g_signal_connect_swapped (dialog, - "response", - G_CALLBACK (gtk_widget_destroy), - dialog); +static void +psppire_missing_val_dialog_class_init (PsppireMissingValDialogClass *class) +{ + GObjectClass *gobject_class; + gobject_class = G_OBJECT_CLASS (class); + + gobject_class->constructor = psppire_missing_val_dialog_constructor; + gobject_class->finalize = psppire_missing_val_dialog_finalize; + gobject_class->set_property = psppire_missing_val_dialog_set_property; + gobject_class->get_property = psppire_missing_val_dialog_get_property; + + g_object_class_install_property ( + gobject_class, PROP_VARIABLE, + g_param_spec_pointer ("variable", + "Variable", + "Variable whose missing values are to be edited. " + "The variable's print format and encoding are also " + "used for editing.", + G_PARAM_WRITABLE)); + + g_object_class_install_property ( + gobject_class, PROP_MISSING_VALUES, + g_param_spec_pointer ("missing-values", + "Missing Values", + "Edited missing values.", + G_PARAM_READABLE)); +} - hbox = gtk_hbox_new (FALSE, 10); +static void +psppire_missing_val_dialog_init (PsppireMissingValDialog *dialog) +{ + /* We do all of our work on widgets in the constructor function, because that + runs after the construction properties have been set. Otherwise + PsppireDialog's "orientation" property hasn't been set and therefore we + have no box to populate. */ + mv_init (&dialog->mvl, 0); + dialog->encoding = NULL; +} + +static void +psppire_missing_val_dialog_finalize (GObject *obj) +{ + PsppireMissingValDialog *dialog = PSPPIRE_MISSING_VAL_DIALOG (obj); + + mv_destroy (&dialog->mvl); + g_free (dialog->encoding); + + G_OBJECT_CLASS (psppire_missing_val_dialog_parent_class)->finalize (obj); +} + +PsppireMissingValDialog * +psppire_missing_val_dialog_new (const struct variable *var) +{ + return PSPPIRE_MISSING_VAL_DIALOG ( + g_object_new (PSPPIRE_TYPE_MISSING_VAL_DIALOG, + "variable", var, + NULL)); +} + +gint +psppire_missing_val_dialog_run (GtkWindow *parent_window, + const struct variable *var, + struct missing_values *mv) +{ + PsppireMissingValDialog *dialog; - gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), - hbox); + dialog = psppire_missing_val_dialog_new (var); + gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + gtk_widget_show (GTK_WIDGET (dialog)); - gtk_box_pack_start (GTK_BOX (hbox), icon, TRUE, FALSE, 10); - gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 10); + 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_show_all (dialog); + gtk_widget_destroy (GTK_WIDGET (dialog)); + return result; } -/* Callback which occurs when the OK button is clicked */ +/* A simple (sub) dialog box for displaying user input errors */ static void -missing_val_dialog_accept (GtkWidget *w, gpointer data) +err_dialog (const gchar *msg, GtkWindow *window) +{ + GtkWidget *dialog = + 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); +} + +/* 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; + + 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. */ +static gboolean +missing_val_dialog_acceptable (gpointer data) { - struct missing_val_dialog *dialog = data; + PsppireMissingValDialog *dialog = data; + int var_width = fmt_var_width (&dialog->format); 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 ) { @@ -99,90 +251,82 @@ missing_val_dialog_accept (GtkWidget *w, gpointer data) continue; } - if ( text_to_value (text, dialog->pv, &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, var_get_width (dialog->pv)); + value_destroy (&v, var_width); } - if ( nvals == 0 || badvals > 0 ) + if ( nvals == 0 ) { - err_dialog (_("Incorrect value for variable type"), - GTK_WINDOW (dialog->window)); - return ; + 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)); - if ( text_to_value (low_text, dialog->pv, &low_val) - && - text_to_value (high_text, dialog->pv, &high_val)) + assert (var_width == 0); /* Ranges are only for numeric variables */ + + if (!try_missing_value(dialog, low_text, &low_val)) { - if ( low_val.f > high_val.f ) - { - err_dialog (_("Incorrect range specification"), - GTK_WINDOW (dialog->window)); - value_destroy (&low_val, var_get_width (dialog->pv)); - value_destroy (&high_val, var_get_width (dialog->pv)); - return ; - } + gtk_widget_grab_focus (dialog->low); + return FALSE; } - else + 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->window)); - value_destroy (&low_val, var_get_width (dialog->pv)); - value_destroy (&high_val, var_get_width (dialog->pv)); - return; + GTK_WINDOW (dialog)); + value_destroy (&low_val, var_width); + value_destroy (&high_val, var_width); + gtk_widget_grab_focus (dialog->low); + return FALSE; } - - discrete_text = - g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->discrete))); - 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, var_get_width (dialog->pv)); - value_destroy (&high_val, var_get_width (dialog->pv)); + discrete_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->discrete))); if ( discrete_text && strlen (g_strstrip (discrete_text)) > 0 ) { union value discrete_val; - if ( !text_to_value (discrete_text, - dialog->pv, - &discrete_val)) + if (!try_missing_value (dialog, discrete_text, &discrete_val)) { - err_dialog (_("Incorrect value for variable type"), - GTK_WINDOW (dialog->window) ); g_free (discrete_text); - value_destroy (&discrete_val, var_get_width (dialog->pv)); - return; + gtk_widget_grab_focus (dialog->discrete); + return FALSE; } mv_add_value (&dialog->mvl, &discrete_val); - value_destroy (&discrete_val, var_get_width (dialog->pv)); + value_destroy (&discrete_val, var_width); } g_free (discrete_text); } - if (gtk_toggle_button_get_active (dialog->button_none)) mv_clear (&dialog->mvl); - var_set_missing_values (dialog->pv, &dialog->mvl); - - gtk_widget_hide (dialog->window); + return TRUE; } @@ -191,7 +335,7 @@ static void discrete (GtkToggleButton *button, gpointer data) { gint i; - struct missing_val_dialog *dialog = data; + PsppireMissingValDialog *dialog = data; for (i = 0 ; i < 3 ; ++i ) { @@ -204,7 +348,7 @@ discrete (GtkToggleButton *button, gpointer data) static void range (GtkToggleButton *button, gpointer data) { - struct missing_val_dialog *dialog = data; + PsppireMissingValDialog *dialog = data; const gboolean active = gtk_toggle_button_get_active (button); @@ -215,40 +359,25 @@ range (GtkToggleButton *button, gpointer data) -/* Callback for when the Missing Value dialog is closed using - the window delete button.*/ -static gint -on_delete (GtkWidget *w, GdkEvent *e, gpointer data) -{ - struct missing_val_dialog *dialog = data; - - gtk_widget_hide (dialog->window); - - return TRUE; -} - - -/* Creates the dialog structure */ -struct missing_val_dialog * -missing_val_dialog_create (GtkWindow *toplevel) +/* Shows the dialog box and sets default values */ +static GObject * +psppire_missing_val_dialog_constructor (GType type, + guint n_properties, + GObjectConstructParam *properties) { - GtkBuilder *xml = builder_new ("var-sheet-dialogs.ui"); - - struct missing_val_dialog *dialog = g_malloc (sizeof (*dialog)); - - dialog->window = get_widget_assert (xml, "missing_values_dialog"); + PsppireMissingValDialog *dialog; + GtkContainer *content_area; + GtkBuilder *xml; + GObject *obj; - gtk_window_set_transient_for - (GTK_WINDOW (dialog->window), toplevel); + obj = G_OBJECT_CLASS (psppire_missing_val_dialog_parent_class)->constructor ( + type, n_properties, properties); + dialog = PSPPIRE_MISSING_VAL_DIALOG (obj); - g_signal_connect_swapped (get_widget_assert (xml, "missing_val_cancel"), - "clicked", G_CALLBACK (gtk_widget_hide), dialog->window); - - g_signal_connect (get_widget_assert (xml, "missing_val_ok"), - "clicked", G_CALLBACK (missing_val_dialog_accept), dialog); - - g_signal_connect (dialog->window, "delete-event", - G_CALLBACK (on_delete), dialog); + 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")); dialog->mv[0] = get_widget_assert (xml, "mv0"); dialog->mv[1] = get_widget_assert (xml, "mv1"); @@ -268,6 +397,9 @@ missing_val_dialog_create (GtkWindow *toplevel) dialog->button_range = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "range_missing")); + psppire_dialog_set_accept_predicate (PSPPIRE_DIALOG (dialog), + missing_val_dialog_acceptable, + dialog); g_signal_connect (dialog->button_discrete, "toggled", G_CALLBACK (discrete), dialog); @@ -277,18 +409,35 @@ missing_val_dialog_create (GtkWindow *toplevel) g_object_unref (xml); - return dialog; + return obj; } -/* Shows the dialog box and sets default values */ void -missing_val_dialog_show (struct missing_val_dialog *dialog) +psppire_missing_val_dialog_set_variable (PsppireMissingValDialog *dialog, + const struct variable *var) { + enum val_type var_type; gint i; - g_return_if_fail (dialog); - g_return_if_fail (dialog->pv); - mv_copy (&dialog->mvl, var_get_missing_values (dialog->pv)); + mv_destroy (&dialog->mvl); + g_free (dialog->encoding); + + if (var != NULL) + { + 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); + } + else + { + mv_init (&dialog->mvl, 0); + dialog->encoding = NULL; + dialog->format = F_8_0; + } /* Blank all entry boxes and make them insensitive */ gtk_entry_set_text (GTK_ENTRY (dialog->low), ""); @@ -298,9 +447,12 @@ missing_val_dialog_show (struct missing_val_dialog *dialog) gtk_widget_set_sensitive (dialog->high, FALSE); gtk_widget_set_sensitive (dialog->discrete, FALSE); + var_type = val_type_from_width (fmt_var_width (&dialog->format)); gtk_widget_set_sensitive (GTK_WIDGET (dialog->button_range), - var_is_numeric (dialog->pv)); + var_type == VAL_NUMERIC); + if (var == NULL) + return; for (i = 0 ; i < 3 ; ++i ) { @@ -316,8 +468,8 @@ missing_val_dialog_show (struct missing_val_dialog *dialog) mv_get_range (&dialog->mvl, &low.f, &high.f); - low_text = value_to_text (low, dialog->pv); - high_text = value_to_text (high, dialog->pv); + low_text = value_to_text__ (low, &dialog->format, dialog->encoding); + high_text = value_to_text__ (high, &dialog->format, dialog->encoding); gtk_entry_set_text (GTK_ENTRY (dialog->low), low_text); gtk_entry_set_text (GTK_ENTRY (dialog->high), high_text); @@ -327,7 +479,8 @@ missing_val_dialog_show (struct missing_val_dialog *dialog) if ( mv_has_value (&dialog->mvl)) { gchar *text; - text = value_to_text (*mv_get_value (&dialog->mvl, 0), dialog->pv); + text = value_to_text__ (*mv_get_value (&dialog->mvl, 0), + &dialog->format, dialog->encoding); gtk_entry_set_text (GTK_ENTRY (dialog->discrete), text); g_free (text); } @@ -348,7 +501,8 @@ missing_val_dialog_show (struct missing_val_dialog *dialog) { gchar *text ; - text = value_to_text (*mv_get_value (&dialog->mvl, i), dialog->pv); + text = value_to_text__ (*mv_get_value (&dialog->mvl, i), + &dialog->format, dialog->encoding); gtk_entry_set_text (GTK_ENTRY (dialog->mv[i]), text); g_free (text); } @@ -360,6 +514,11 @@ missing_val_dialog_show (struct missing_val_dialog *dialog) { gtk_toggle_button_set_active (dialog->button_none, TRUE); } +} - gtk_widget_show (dialog->window); +const struct missing_values * +psppire_missing_val_dialog_get_missing_values ( + const PsppireMissingValDialog *dialog) +{ + return &dialog->mvl; }