From 523bde2a5dc354528b1535afc8643a8617e1cefe Mon Sep 17 00:00:00 2001 From: John Darrington Date: Fri, 1 Jul 2011 18:39:25 +0200 Subject: [PATCH] Added COUNT dialog box. Addresses one part of bug #29869 --- src/ui/gui/automake.mk | 3 + src/ui/gui/count-dialog.c | 293 +++++++++++++++++++++++++++++ src/ui/gui/count-dialog.h | 24 +++ src/ui/gui/count.ui | 304 +++++++++++++++++++++++++++++++ src/ui/gui/data-editor.ui | 7 + src/ui/gui/psppire-data-window.c | 3 + src/ui/gui/psppire-val-chooser.c | 79 +++++--- src/ui/gui/psppire-val-chooser.h | 1 + src/ui/gui/recode-dialog.c | 2 +- 9 files changed, 694 insertions(+), 22 deletions(-) create mode 100644 src/ui/gui/count-dialog.c create mode 100644 src/ui/gui/count-dialog.h create mode 100644 src/ui/gui/count.ui diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index 5eccb206..56d07447 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -7,6 +7,7 @@ UI_FILES = \ src/ui/gui/binomial.ui \ src/ui/gui/compute.ui \ src/ui/gui/correlation.ui \ + src/ui/gui/count.ui \ src/ui/gui/crosstabs.ui \ src/ui/gui/chi-square.ui \ src/ui/gui/descriptives.ui \ @@ -133,6 +134,8 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/chi-square-dialog.h \ src/ui/gui/correlation-dialog.c \ src/ui/gui/correlation-dialog.h \ + src/ui/gui/count-dialog.c \ + src/ui/gui/count-dialog.h \ src/ui/gui/crosstabs-dialog.c \ src/ui/gui/crosstabs-dialog.h \ src/ui/gui/customentry.c \ diff --git a/src/ui/gui/count-dialog.c b/src/ui/gui/count-dialog.c new file mode 100644 index 00000000..39a99dbd --- /dev/null +++ b/src/ui/gui/count-dialog.c @@ -0,0 +1,293 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2011 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + + +#include + +#include "count-dialog.h" + +#include +#include "helper.h" +#include "psppire-dialog.h" +#include "psppire-selector.h" +#include "psppire-val-chooser.h" +#include "psppire-var-view.h" +#include "psppire-acr.h" +#include "dialog-common.h" + +#include +#include "executor.h" + +struct cnt_dialog +{ + PsppireDict *dict; + + GtkWidget *dialog; + + GtkListStore *value_list; + GtkWidget *chooser; + + GtkWidget *target; + GtkWidget *label; + GtkWidget *variable_treeview; +}; + +/* Callback which gets called when a new row is selected + in the acr's variable treeview. + We use if to set the togglebuttons and entries to correspond to the + selected row. +*/ +static void +on_acr_selection_change (GtkTreeSelection *selection, gpointer data) +{ + GtkTreeIter iter; + struct old_value *ov = NULL; + GtkTreeModel *model = NULL; + struct cnt_dialog *cnt = data; + GValue ov_value = {0}; + + if ( ! gtk_tree_selection_get_selected (selection, &model, &iter) ) + return; + + gtk_tree_model_get_value (GTK_TREE_MODEL (model), &iter, + 0, &ov_value); + + ov = g_value_get_boxed (&ov_value); + psppire_val_chooser_set_status (PSPPIRE_VAL_CHOOSER (cnt->chooser), ov); +} + + + +static char * generate_syntax (const struct cnt_dialog *cnt); + +static void values_dialog (struct cnt_dialog *cd); + +static void +refresh (PsppireDialog *dialog, struct cnt_dialog *cnt) +{ + GtkTreeModel *vars = + gtk_tree_view_get_model (GTK_TREE_VIEW (cnt->variable_treeview)); + + gtk_list_store_clear (GTK_LIST_STORE (vars)); + + gtk_entry_set_text (GTK_ENTRY (cnt->target), ""); + gtk_entry_set_text (GTK_ENTRY (cnt->label), ""); + gtk_list_store_clear (GTK_LIST_STORE (cnt->value_list)); +} + +static gboolean +dialog_state_valid (gpointer data) +{ + GtkTreeIter iter; + struct cnt_dialog *cnt = data; + + if (! cnt->value_list) + return FALSE; + + if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cnt->value_list), &iter) ) + return FALSE; + + if (!gtk_tree_model_get_iter_first (gtk_tree_view_get_model (GTK_TREE_VIEW (cnt->variable_treeview)), &iter)) + return FALSE; + + if (0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (cnt->target)))) + return FALSE; + + return TRUE; +} + +void count_dialog (PsppireDataWindow *de) +{ + gint response; + PsppireVarStore *vs = NULL; + struct cnt_dialog cnt; + + GtkBuilder *builder = builder_new ("count.ui"); + + GtkWidget *selector = get_widget_assert (builder, "count-selector1"); + + GtkWidget *dict_view = get_widget_assert (builder, "dict-view"); + GtkWidget *button = get_widget_assert (builder, "button1"); + + cnt.target = get_widget_assert (builder, "entry1"); + cnt.label = get_widget_assert (builder, "entry2"); + cnt.variable_treeview = get_widget_assert (builder, "treeview2"); + + g_signal_connect_swapped (button, "clicked", G_CALLBACK (values_dialog), &cnt); + + cnt.value_list = gtk_list_store_new (1,old_value_get_type ()); + + cnt.dialog = get_widget_assert (builder, "count-dialog"); + + g_signal_connect (cnt.dialog, "refresh", G_CALLBACK (refresh), &cnt); + + + g_object_get (de->data_editor, "var-store", &vs, NULL); + + g_object_get (vs, "dictionary", &cnt.dict, NULL); + + gtk_window_set_transient_for (GTK_WINDOW (cnt.dialog), GTK_WINDOW (de)); + + g_object_set (dict_view, "model", cnt.dict, NULL); + + psppire_selector_set_allow (PSPPIRE_SELECTOR (selector), numeric_only); + + psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (cnt.dialog), + dialog_state_valid, &cnt); + + response = psppire_dialog_run (PSPPIRE_DIALOG (cnt.dialog)); + + switch (response) + { + case GTK_RESPONSE_OK: + g_free (execute_syntax_string (de, generate_syntax (&cnt))); + break; + case PSPPIRE_RESPONSE_PASTE: + g_free (paste_syntax_to_window (generate_syntax (&cnt))); + break; + default: + break; + } + + + g_object_unref (builder); +} + +/* A function to set a value in a column in the ACR */ +static gboolean +set_value (gint col, GValue *val, gpointer data) +{ + struct cnt_dialog *cnt = data; + PsppireValChooser *vc = PSPPIRE_VAL_CHOOSER (cnt->chooser); + struct old_value ov; + + g_assert (col == 0); + + psppire_val_chooser_get_status (vc, &ov); + + g_value_init (val, old_value_get_type ()); + g_value_set_boxed (val, &ov); + + return TRUE; +} + + +static void +values_dialog (struct cnt_dialog *cd) +{ + gint response; + GtkListStore *local_store = clone_list_store (cd->value_list); + GtkBuilder *builder = builder_new ("count.ui"); + + GtkWidget *dialog = get_widget_assert (builder, "values-dialog"); + + GtkWidget *acr = get_widget_assert (builder, "acr"); + cd->chooser = get_widget_assert (builder, "value-chooser"); + + psppire_acr_set_enabled (PSPPIRE_ACR (acr), TRUE); + + psppire_acr_set_model (PSPPIRE_ACR (acr), local_store); + psppire_acr_set_get_value_func (PSPPIRE_ACR (acr), set_value, cd); + + { + GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (PSPPIRE_ACR(acr)->tv)); + g_signal_connect (sel, "changed", + G_CALLBACK (on_acr_selection_change), cd); + } + + response = psppire_dialog_run (PSPPIRE_DIALOG (dialog)); + + if ( response == PSPPIRE_RESPONSE_CONTINUE ) + { + g_object_unref (cd->value_list); + cd->value_list = local_store; + } + else + { + g_object_unref (local_store); + } + + psppire_dialog_notify_change (PSPPIRE_DIALOG (cd->dialog)); + + g_object_unref (builder); +} + + + +static char * +generate_syntax (const struct cnt_dialog *cnt) +{ + gchar *text = NULL; + const gchar *s = NULL; + gboolean ok; + GtkTreeIter iter; + GString *str = g_string_sized_new (100); + + g_string_append (str, "\nCOUNT "); + + g_string_append (str, gtk_entry_get_text (GTK_ENTRY (cnt->target))); + + g_string_append (str, " ="); + + psppire_var_view_append_names (PSPPIRE_VAR_VIEW (cnt->variable_treeview), 0, str); + + g_string_append (str, "("); + for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cnt->value_list), + &iter); + ok; + ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (cnt->value_list), &iter)) + { + GValue a_value = {0}; + struct old_value *ov; + + gtk_tree_model_get_value (GTK_TREE_MODEL (cnt->value_list), &iter, + 0, &a_value); + + ov = g_value_get_boxed (&a_value); + + g_string_append (str, " "); + old_value_append_syntax (str, ov); + } + g_string_append (str, ")."); + + + s = gtk_entry_get_text (GTK_ENTRY (cnt->label)); + if (0 != strcmp (s, "")) + { + struct string ds; + ds_init_empty (&ds); + g_string_append (str, "\nVARIABLE LABELS "); + + g_string_append (str, gtk_entry_get_text (GTK_ENTRY (cnt->target))); + + g_string_append (str, " "); + + syntax_gen_string (&ds, ss_cstr (s)); + + g_string_append (str, ds_cstr (&ds)); + + g_string_append (str, "."); + ds_destroy (&ds); + } + + g_string_append (str, "\nEXECUTE.\n"); + + text = str->str; + + g_string_free (str, FALSE); + + return text; +} diff --git a/src/ui/gui/count-dialog.h b/src/ui/gui/count-dialog.h new file mode 100644 index 00000000..d97765d6 --- /dev/null +++ b/src/ui/gui/count-dialog.h @@ -0,0 +1,24 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2011 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef __COUNT_DIALOG_H +#define __COUNT_DIALOG_H + +#include "psppire-data-window.h" + +void count_dialog (PsppireDataWindow * data); + +#endif diff --git a/src/ui/gui/count.ui b/src/ui/gui/count.ui new file mode 100644 index 00000000..13ab1313 --- /dev/null +++ b/src/ui/gui/count.ui @@ -0,0 +1,304 @@ + + + + + + + Count Occurrences of Values within Cases + True + + + True + vertical + + + True + 5 + 5 + 5 + 5 + + + True + 4 + 3 + + + True + True + never + automatic + etched-in + + + True + True + 5 + False + False + + + + + 1 + 4 + + + + + True + True + True + True + 5 + dict-view + treeview2 + + + 1 + 2 + 1 + 2 + + + + + + + True + 0 + none + + + True + 12 + + + True + True + never + automatic + etched-in + + + True + True + False + False + + + + + + + + + True + Numeric _Variables: + True + True + treeview2 + + + + + 2 + 3 + 1 + 2 + + + + + True + 0 + none + + + True + 12 + + + True + True + + + + + + + + True + _Target Variable: + True + True + entry1 + + + + + GTK_SHRINK + + + + + True + 0 + none + + + True + 12 + + + True + True + + + + + + + + True + Target _Label: + True + True + entry2 + + + + + 2 + 3 + GTK_SHRINK + + + + + _Define Values... + True + True + True + True + + + 2 + 3 + 2 + 3 + GTK_EXPAND + + + + + + + + + + + + + + + + + + + + 0 + + + + + True + 5 + + + False + False + end + 1 + + + + + + + Count Values within Cases: Values to Count + True + Vertical + + + True + vertical + 2 + + + True + + + True + False + Value + + + 0 + 5 + + + + + True + 0 + etched-in + + + True + 12 + + + True + 5 + + + + + + + True + Values _to Count: + True + True + acr + + + + + 1 + + + + + 0 + + + + + True + 5 + PSPPIRE_BUTTON_CONTINUE_MASK | PSPPIRE_BUTTON_CANCEL_MASK | PSPPIRE_BUTTON_HELP_MASK + PSPPIRE_BUTTON_CONTINUE_MASK + + + False + False + end + 1 + + + + + + diff --git a/src/ui/gui/data-editor.ui b/src/ui/gui/data-editor.ui index 012605e5..670b4b86 100644 --- a/src/ui/gui/data-editor.ui +++ b/src/ui/gui/data-editor.ui @@ -294,6 +294,12 @@ _Compute... + + + transform_count + Cou_nt... + + transform_rank @@ -537,6 +543,7 @@ + diff --git a/src/ui/gui/psppire-data-window.c b/src/ui/gui/psppire-data-window.c index 7fc3ab83..21626fe2 100644 --- a/src/ui/gui/psppire-data-window.c +++ b/src/ui/gui/psppire-data-window.c @@ -30,6 +30,7 @@ #include "ui/gui/comments-dialog.h" #include "ui/gui/compute-dialog.h" #include "ui/gui/correlation-dialog.h" +#include "ui/gui/count-dialog.h" #include "ui/gui/crosstabs-dialog.h" #include "ui/gui/descriptives-dialog.h" #include "ui/gui/entry-dialog.h" @@ -1085,6 +1086,8 @@ psppire_data_window_finish_init (PsppireDataWindow *de, connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog)); connect_action (de, "transform_rank", G_CALLBACK (rank_dialog)); + + connect_action (de, "transform_count", G_CALLBACK (count_dialog)); connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog)); diff --git a/src/ui/gui/psppire-val-chooser.c b/src/ui/gui/psppire-val-chooser.c index 368eea32..59225e27 100644 --- a/src/ui/gui/psppire-val-chooser.c +++ b/src/ui/gui/psppire-val-chooser.c @@ -77,9 +77,21 @@ enum { PROP_0, PROP_IS_STRING, + PROP_SHOW_ELSE }; +enum + { + VC_VALUE, + VC_SYSMIS, + VC_MISSING, + VC_RANGE, + VC_LOW_UP, + VC_HIGH_DOWN, + VC_ELSE + }; + static void psppire_val_chooser_set_property (GObject *object, guint prop_id, @@ -90,14 +102,20 @@ psppire_val_chooser_set_property (GObject *object, switch (prop_id) { + case PROP_SHOW_ELSE: + { + gboolean x = g_value_get_boolean (value); + gtk_widget_set_visible (GTK_WIDGET (vr->rw[VC_ELSE].rb), x); + gtk_widget_set_visible (GTK_WIDGET (vr->rw[VC_ELSE].label), x); + } + break; case PROP_IS_STRING: vr->input_var_is_string = g_value_get_boolean (value); - - gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[1].rb), !vr->input_var_is_string); - gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[2].rb), !vr->input_var_is_string); - gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[3].rb), !vr->input_var_is_string); - gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[4].rb), !vr->input_var_is_string); - gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[5].rb), !vr->input_var_is_string); + gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_SYSMIS].rb), !vr->input_var_is_string); + gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_MISSING].rb), !vr->input_var_is_string); + gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_RANGE].rb), !vr->input_var_is_string); + gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_LOW_UP].rb), !vr->input_var_is_string); + gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[VC_HIGH_DOWN].rb), !vr->input_var_is_string); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -116,6 +134,13 @@ psppire_val_chooser_get_property (GObject *object, switch (prop_id) { + case PROP_SHOW_ELSE: + { + gboolean x = + gtk_widget_get_visible (GTK_WIDGET (vr->rw[VC_ELSE].rb)); + g_value_set_boolean (value, x); + } + break; case PROP_IS_STRING: g_value_set_boolean (value, vr->input_var_is_string); default: @@ -140,6 +165,14 @@ psppire_val_chooser_class_init (PsppireValChooserClass *class) FALSE, G_PARAM_READWRITE); + GParamSpec *show_else_spec = + g_param_spec_boolean ("show-else", + "Show Else", + "Should the \"All other values\" item be visible", + TRUE, + G_PARAM_READWRITE); + + parent_class = g_type_class_peek_parent (class); object_class->set_property = psppire_val_chooser_set_property; @@ -150,6 +183,10 @@ psppire_val_chooser_class_init (PsppireValChooserClass *class) g_object_class_install_property (object_class, PROP_IS_STRING, is_string_spec); + + g_object_class_install_property (object_class, + PROP_SHOW_ELSE, + show_else_spec); } @@ -321,13 +358,13 @@ psppire_val_chooser_init (PsppireValChooser *vr) for (i = 0; i < n_VAL_CHOOSER_BUTTONS; ++i) { struct layout *l = &range_opt[i]; - GtkWidget *label = gtk_label_new (gettext (l->label)); + vr->rw[i].label = GTK_LABEL (gtk_label_new (gettext (l->label))); vr->rw[i].rb = GTK_TOGGLE_BUTTON (gtk_radio_button_new (group)); - gtk_widget_set_sensitive (label, FALSE); - g_signal_connect (vr->rw[i].rb, "toggled", G_CALLBACK (set_sensitivity_from_toggle), label); + gtk_widget_set_sensitive (GTK_WIDGET (vr->rw[i].label), FALSE); + g_signal_connect (vr->rw[i].rb, "toggled", G_CALLBACK (set_sensitivity_from_toggle), vr->rw[i].label); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_misc_set_alignment (GTK_MISC (vr->rw[i].label), 0, 0.5); group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (vr->rw[i].rb)); @@ -335,7 +372,7 @@ psppire_val_chooser_init (PsppireValChooser *vr) row, row + 1); - gtk_table_attach_defaults (GTK_TABLE (table), label, 1, 2, + gtk_table_attach_defaults (GTK_TABLE (table), GTK_WIDGET (vr->rw[i].label), 1, 2, row, row + 1); ++row; @@ -619,23 +656,23 @@ psppire_val_chooser_set_status (PsppireValChooser *vr, const struct old_value *o break; case OV_SYSMIS: - gtk_toggle_button_set_active (vr->rw[1].rb, TRUE); + gtk_toggle_button_set_active (vr->rw[VC_SYSMIS].rb, TRUE); break; case OV_MISSING: - gtk_toggle_button_set_active (vr->rw[2].rb, TRUE); + gtk_toggle_button_set_active (vr->rw[VC_MISSING].rb, TRUE); break; case OV_RANGE: { gchar *str = num_to_string (ov->v.range[0]); - gtk_toggle_button_set_active (vr->rw[3].rb, TRUE); - gtk_entry_set_text (vr->rw[3].e1, str); + gtk_toggle_button_set_active (vr->rw[VC_RANGE].rb, TRUE); + gtk_entry_set_text (vr->rw[VC_RANGE].e1, str); g_free (str); str = num_to_string (ov->v.range[1]); - gtk_entry_set_text (vr->rw[3].e2, str); + gtk_entry_set_text (vr->rw[VC_RANGE].e2, str); g_free (str); } break; @@ -644,9 +681,9 @@ psppire_val_chooser_set_status (PsppireValChooser *vr, const struct old_value *o { gchar *str = num_to_string (ov->v.range[1]); - gtk_toggle_button_set_active (vr->rw[4].rb, TRUE); + gtk_toggle_button_set_active (vr->rw[VC_LOW_UP].rb, TRUE); - gtk_entry_set_text (vr->rw[4].e1, str); + gtk_entry_set_text (vr->rw[VC_LOW_UP].e1, str); g_free (str); } @@ -657,16 +694,16 @@ psppire_val_chooser_set_status (PsppireValChooser *vr, const struct old_value *o { gchar *str = num_to_string (ov->v.range[0]); - gtk_toggle_button_set_active (vr->rw[5].rb, TRUE); + gtk_toggle_button_set_active (vr->rw[VC_HIGH_DOWN].rb, TRUE); - gtk_entry_set_text (vr->rw[5].e1, str); + gtk_entry_set_text (vr->rw[VC_HIGH_DOWN].e1, str); g_free (str); } break; case OV_ELSE: - gtk_toggle_button_set_active (vr->rw[6].rb, TRUE); + gtk_toggle_button_set_active (vr->rw[VC_ELSE].rb, TRUE); break; default: diff --git a/src/ui/gui/psppire-val-chooser.h b/src/ui/gui/psppire-val-chooser.h index 6ef546ee..5c8175ba 100644 --- a/src/ui/gui/psppire-val-chooser.h +++ b/src/ui/gui/psppire-val-chooser.h @@ -43,6 +43,7 @@ typedef struct _PsppireValChooserClass PsppireValChooserClass; struct range_widgets { + GtkLabel *label; GtkToggleButton *rb; GtkEntry *e1; GtkEntry *e2; diff --git a/src/ui/gui/recode-dialog.c b/src/ui/gui/recode-dialog.c index d92517c7..dc14067d 100644 --- a/src/ui/gui/recode-dialog.c +++ b/src/ui/gui/recode-dialog.c @@ -860,7 +860,7 @@ set_new_value (GValue *val, const struct recode_dialog *rd) /* A function to set a value in a column in the ACR */ -gboolean +static gboolean set_value (gint col, GValue *val, gpointer data) { struct recode_dialog *rd = data; -- 2.30.2