1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2015 Free Software Foundation
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.
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.
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/>. */
20 #include "psppire-dialog-action-aggregate.h"
22 #include "dialog-common.h"
24 #include <language/stats/aggregate.h>
26 #include "psppire-var-view.h"
27 #include "psppire-selector.h"
28 #include "psppire-acr.h"
30 #include "psppire-dialog.h"
31 #include "builder-wrapper.h"
33 #include <ui/syntax-gen.h>
34 #include <libpspp/str.h>
37 #include <gl/c-xvasprintf.h>
41 #define _(msgid) gettext (msgid)
42 #define N_(msgid) msgid
45 static void psppire_dialog_action_aggregate_init (PsppireDialogActionAggregate *act);
46 static void psppire_dialog_action_aggregate_class_init (PsppireDialogActionAggregateClass *class);
49 G_DEFINE_TYPE (PsppireDialogActionAggregate, psppire_dialog_action_aggregate, PSPPIRE_TYPE_DIALOG_ACTION);
51 static void append_summary_spec (const PsppireDialogActionAggregate *agg, GtkTreeIter *iter, GString *string);
54 append_summary_variable_syntax (const PsppireDialogActionAggregate *agg, GString *string)
57 GtkTreeModel *acr_model = GTK_TREE_MODEL (PSPPIRE_ACR (agg->summary_acr)->list_store);
62 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (acr_model), &iter);
64 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (acr_model), &iter)
67 g_string_append (string, "\n\t/");
69 append_summary_spec (agg, &iter, string);
74 append_destination_filename (const PsppireDialogActionAggregate *agg, GString *gs)
76 if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (agg->filename_radiobutton)))
79 const gchar *s = gtk_label_get_text (GTK_LABEL (agg->filename_label));
81 syntax_gen_string (&ss, ss_cstr (s));
82 g_string_append (gs, ds_cstr (&ss));
87 g_string_append (gs, "* ");
89 if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (agg->replace_radiobutton)))
90 g_string_append (gs, "MODE=REPLACE");
92 g_string_append (gs, "MODE=ADDVARIABLES");
98 generate_syntax (const PsppireDialogAction *act)
100 PsppireDialogActionAggregate *agg = PSPPIRE_DIALOG_ACTION_AGGREGATE (act);
104 GString *string = g_string_new ("AGGREGATE OUTFILE=");
106 append_destination_filename (agg, string);
108 if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (agg->sorted_button)))
109 g_string_append (string, "\n\t/PRESORTED");
111 g_string_append (string, "\n\t/BREAK=");
113 psppire_var_view_append_names (PSPPIRE_VAR_VIEW (agg->break_variables), 0, string);
115 append_summary_variable_syntax (agg, string);
117 g_string_append (string, ".\n");
121 g_string_free (string, FALSE);
128 dialog_state_valid (gpointer user_data)
130 PsppireDialogActionAggregate *agg = user_data;
132 GtkTreeModel *liststore =
133 gtk_tree_view_get_model (GTK_TREE_VIEW (agg->break_variables));
135 if ( ! gtk_tree_model_get_iter_first (liststore, &iter))
138 liststore = GTK_TREE_MODEL (PSPPIRE_ACR (agg->summary_acr)->list_store);
140 if ( ! gtk_tree_model_get_iter_first (liststore, &iter))
146 static void update_arguments (PsppireDialogActionAggregate *agg);
150 refresh (PsppireDialogAction *fd_)
152 PsppireDialogActionAggregate *agg = PSPPIRE_DIALOG_ACTION_AGGREGATE (fd_);
154 GtkTreeModel *liststore =
155 gtk_tree_view_get_model (GTK_TREE_VIEW (agg->break_variables));
156 gtk_list_store_clear (GTK_LIST_STORE (liststore));
158 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (agg->add_radiobutton), TRUE);
159 gtk_label_set_text (GTK_LABEL (agg->filename_label), "");
162 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (agg->needs_sort_button), TRUE);
164 gtk_entry_set_text (GTK_ENTRY (agg->summary_sv_entry), "");
165 gtk_entry_set_text (GTK_ENTRY (agg->summary_arg1_entry), "");
166 gtk_entry_set_text (GTK_ENTRY (agg->summary_arg2_entry), "");
167 gtk_entry_set_text (GTK_ENTRY (agg->summary_var_label_entry), "");
168 gtk_entry_set_text (GTK_ENTRY (agg->summary_var_name_entry), "N_BREAK");
169 gtk_editable_select_region (GTK_EDITABLE (agg->summary_var_name_entry), 0, -1);
171 gtk_combo_box_set_active (GTK_COMBO_BOX (agg->function_combo), N);
173 gtk_list_store_clear (PSPPIRE_ACR (agg->summary_acr)->list_store);
175 update_arguments (agg);
180 COMBO_MODEL_COL_DESC = 0,
181 COMBO_MODEL_COL_SYNTAX,
182 COMBO_MODEL_COL_SRC_VARS,
183 COMBO_MODEL_COL_ARITY
190 render_summary (GtkTreeViewColumn *tree_column,
191 GtkCellRenderer *cell,
192 GtkTreeModel *tree_model,
196 PsppireDialogActionAggregate *agg = data;
198 GString *string = g_string_new ("");
200 append_summary_spec (agg, iter, string);
203 g_object_set (cell, "text", string->str, NULL);
205 g_string_free (string, TRUE);
209 choose_filename (PsppireDialogActionAggregate *fd)
211 GtkFileFilter *filter;
213 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Aggregate destination file"),
214 GTK_WINDOW (PSPPIRE_DIALOG_ACTION (fd)->toplevel),
215 GTK_FILE_CHOOSER_ACTION_SAVE,
216 _("Cancel"), GTK_RESPONSE_CANCEL,
217 _("Save"), GTK_RESPONSE_ACCEPT,
220 g_object_set (dialog, "local-only", FALSE, NULL);
222 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
225 filter = gtk_file_filter_new ();
226 gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
227 gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
228 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
230 filter = gtk_file_filter_new ();
231 gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
232 gtk_file_filter_add_pattern (filter, "*.zsav");
233 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
235 filter = gtk_file_filter_new ();
236 gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
237 gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
238 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
241 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
245 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
247 gtk_label_set_text (GTK_LABEL (fd->filename_label), filename);
253 gtk_widget_destroy (dialog);
258 populate_combo_model (GtkComboBox *cb)
260 GtkListStore *list = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
262 const struct agr_func *af = agr_func_tab;
263 GtkCellRenderer *renderer ;
265 for (af = agr_func_tab; af->name; ++af)
267 const gchar *s = af->description;
271 gtk_list_store_append (list, &iter);
272 gtk_list_store_set (list, &iter,
273 COMBO_MODEL_COL_DESC, gettext (s),
274 COMBO_MODEL_COL_SYNTAX, af->name,
275 COMBO_MODEL_COL_SRC_VARS, af->src_vars,
276 COMBO_MODEL_COL_ARITY, af->n_args,
280 renderer = gtk_cell_renderer_text_new ();
281 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cb), renderer, FALSE);
283 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cb), renderer, "text", 0);
285 gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
286 g_object_unref (list);
292 SUMMARY_COL_VARNAME = 0,
293 SUMMARY_COL_VARLABEL,
300 /* Set VAL to the value appropriate for COL according to the
301 current state of the dialog */
303 get_summary_spec (gint col, GValue *val, gpointer data)
305 PsppireDialogActionAggregate *agg = PSPPIRE_DIALOG_ACTION_AGGREGATE (data);
308 case SUMMARY_COL_VARNAME:
309 g_value_init (val, G_TYPE_STRING);
310 g_value_set_string (val, gtk_entry_get_text (GTK_ENTRY (agg->summary_var_name_entry)));
312 case SUMMARY_COL_VARLABEL:
313 g_value_init (val, G_TYPE_STRING);
314 g_value_set_string (val, gtk_entry_get_text (GTK_ENTRY (agg->summary_var_label_entry)));
316 case SUMMARY_COL_SRCVAR:
317 g_value_init (val, G_TYPE_STRING);
318 g_value_set_string (val, gtk_entry_get_text (GTK_ENTRY (agg->summary_sv_entry)));
320 case SUMMARY_COL_FUNCIDX:
321 g_value_init (val, G_TYPE_INT);
322 g_value_set_int (val, gtk_combo_box_get_active (GTK_COMBO_BOX (agg->function_combo)));
324 case SUMMARY_COL_ARG1:
326 const gchar *text = gtk_entry_get_text (GTK_ENTRY (agg->summary_arg1_entry));
327 g_value_init (val, G_TYPE_DOUBLE);
328 g_value_set_double (val, g_strtod (text, 0));
331 case SUMMARY_COL_ARG2:
333 const gchar *text = gtk_entry_get_text (GTK_ENTRY (agg->summary_arg2_entry));
334 g_value_init (val, G_TYPE_DOUBLE);
335 g_value_set_double (val, g_strtod (text, 0));
339 g_assert_not_reached ();
346 /* Returns TRUE iff all the necessary controls have been set to
347 completely specify a summary function */
349 summary_complete (const PsppireDialogActionAggregate *agg)
353 enum agr_src_vars src_vars;
355 GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (agg->function_combo));
357 if ( 0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (agg->summary_var_name_entry))))
360 ok = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (agg->function_combo), &iter);
367 gtk_tree_model_get (model,
369 COMBO_MODEL_COL_ARITY, &n_args,
370 COMBO_MODEL_COL_SRC_VARS, &src_vars,
373 if ( src_vars == AGR_SV_YES )
375 if (0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (agg->summary_sv_entry))))
381 if (0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (agg->summary_arg2_entry))))
387 if (0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (agg->summary_arg1_entry))))
397 /* Enable/Disable the summary variable ACR */
399 update_acr (PsppireDialogActionAggregate *agg)
401 gboolean ready = summary_complete (agg);
403 psppire_acr_set_enabled (PSPPIRE_ACR (agg->summary_acr), ready);
407 /* Update the status of the dialog box according to what row of the ACR's treeview
410 on_acr_change (const PsppireDialogActionAggregate *agg, GtkTreeView *tv)
412 const gchar *varname = "";
413 const gchar *label = "";
414 const gchar *srcvar = "";
417 gchar *text1 = g_strdup ("");
418 gchar *text2 = g_strdup ("");
421 GtkTreeModel *model = gtk_tree_view_get_model (tv);
422 GtkTreeSelection *sel = gtk_tree_view_get_selection (tv);
424 if (gtk_tree_selection_get_selected (sel, &model, &iter))
426 gtk_tree_model_get (model, &iter,
427 SUMMARY_COL_VARNAME, &varname,
428 SUMMARY_COL_VARLABEL, &label,
429 SUMMARY_COL_FUNCIDX, &f_idx,
430 SUMMARY_COL_SRCVAR, &srcvar,
431 SUMMARY_COL_ARG1, &arg1,
432 SUMMARY_COL_ARG2, &arg2, -1);
434 gtk_entry_set_text (GTK_ENTRY (agg->summary_var_name_entry), varname);
435 gtk_entry_set_text (GTK_ENTRY (agg->summary_var_label_entry), label);
436 gtk_entry_set_text (GTK_ENTRY (agg->summary_sv_entry), srcvar);
438 text1 = c_xasprintf ("%.*g", DBL_DIG + 1, arg1);
439 text2 = c_xasprintf ("%.*g", DBL_DIG + 1, arg2);
442 gtk_entry_set_text (GTK_ENTRY (agg->summary_arg1_entry), text1);
445 gtk_entry_set_text (GTK_ENTRY (agg->summary_arg2_entry), text2);
448 gtk_combo_box_set_active (GTK_COMBO_BOX (agg->function_combo), f_idx);
452 /* Update the sensitivity of the summary variable argument fields */
454 update_arguments (PsppireDialogActionAggregate *agg)
458 gboolean ok = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (agg->function_combo), &iter);
462 GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (agg->function_combo));
464 enum agr_src_vars src_vars;
465 gtk_tree_model_get (model,
467 COMBO_MODEL_COL_ARITY, &n_args,
468 COMBO_MODEL_COL_SRC_VARS, &src_vars,
471 gtk_widget_set_sensitive (agg->summary_sv, src_vars != AGR_SV_NO);
472 gtk_widget_set_sensitive (agg->summary_arg2, n_args >= 2);
473 gtk_widget_set_sensitive (agg->summary_arg1, n_args >= 1);
477 gtk_widget_set_sensitive (agg->summary_sv, FALSE);
478 gtk_widget_set_sensitive (agg->summary_arg2, FALSE);
479 gtk_widget_set_sensitive (agg->summary_arg1, FALSE);
486 psppire_dialog_action_aggregate_activate (PsppireDialogAction *a)
488 PsppireDialogActionAggregate *act = PSPPIRE_DIALOG_ACTION_AGGREGATE (a);
489 PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a);
491 GHashTable *thing = psppire_dialog_action_get_hash_table (pda);
492 GtkBuilder *xml = g_hash_table_lookup (thing, a);
495 xml = builder_new ("aggregate.ui");
496 g_hash_table_insert (thing, a, xml);
499 pda->dialog = get_widget_assert (xml, "aggregate-dialog");
500 pda->source = get_widget_assert (xml, "dict-view");
504 GtkWidget *break_selector = get_widget_assert (xml, "break-selector");
506 act->pane = get_widget_assert (xml, "hbox1");
508 act->break_variables = get_widget_assert (xml, "psppire-var-view1");
509 act->filename_radiobutton = get_widget_assert (xml, "filename-radiobutton");
510 act->filename_button = get_widget_assert (xml, "filename-button");
511 act->filename_box = get_widget_assert (xml, "filename-box");
512 act->filename_label = get_widget_assert (xml, "filename-label");
513 act->replace_radiobutton = get_widget_assert (xml, "replace-radiobutton");
514 act->add_radiobutton = get_widget_assert (xml, "add-radiobutton");
515 act->function_combo = get_widget_assert (xml, "function-combo");
517 act->summary_acr = get_widget_assert (xml, "psppire-acr1");
518 act->summary_var_name_entry = get_widget_assert (xml, "summary-var-name-entry");
520 act->summary_arg1 = get_widget_assert (xml, "summary-arg1");
521 act->summary_arg2 = get_widget_assert (xml, "summary-arg2");
523 act->summary_arg1_entry = get_widget_assert (xml, "summary-arg-entry1");
524 act->summary_arg2_entry = get_widget_assert (xml, "summary-arg-entry2");
526 act->summary_var_label_entry = get_widget_assert (xml, "summary-var-label-entry");
528 act->summary_sv = get_widget_assert (xml, "source-var");
529 act->summary_sv_entry = get_widget_assert (xml, "source-var-entry");
531 act->sorted_button = get_widget_assert (xml, "sorted-radiobutton");
532 act->needs_sort_button = get_widget_assert (xml, "needs-sort-radiobutton");
535 GtkTreeViewColumn *column ;
539 GtkCellRenderer *cell_renderer ;
541 GtkListStore *list = gtk_list_store_new (6,
549 psppire_acr_set_model (PSPPIRE_ACR (act->summary_acr), list);
550 g_object_unref (list);
552 psppire_acr_set_get_value_func (PSPPIRE_ACR (act->summary_acr),
553 get_summary_spec, act);
555 column = gtk_tree_view_get_column (PSPPIRE_ACR (act->summary_acr)->tv, 0);
557 l = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
559 cell_renderer = l->data;
561 gtk_tree_view_column_set_cell_data_func (column,
567 g_signal_connect_swapped (PSPPIRE_ACR (act->summary_acr)->tv,
568 "cursor-changed", G_CALLBACK (on_acr_change), act);
571 g_signal_connect_swapped (act->summary_var_name_entry, "changed", G_CALLBACK (update_acr), act);
572 g_signal_connect_swapped (act->function_combo, "changed", G_CALLBACK (update_acr), act);
573 g_signal_connect_swapped (act->summary_sv_entry, "changed", G_CALLBACK (update_acr), act);
574 g_signal_connect_swapped (act->summary_arg1_entry, "changed", G_CALLBACK (update_acr), act);
575 g_signal_connect_swapped (act->summary_arg2_entry, "changed", G_CALLBACK (update_acr), act);
578 g_signal_connect_swapped (act->function_combo, "changed",
579 G_CALLBACK (update_arguments), act);
581 populate_combo_model (GTK_COMBO_BOX (act->function_combo));
584 psppire_selector_set_filter_func (PSPPIRE_SELECTOR (break_selector), NULL);
587 g_signal_connect (act->filename_radiobutton, "toggled",
588 G_CALLBACK (set_sensitivity_from_toggle), act->filename_box );
590 g_signal_connect_swapped (act->filename_button, "clicked",
591 G_CALLBACK (choose_filename), act);
593 psppire_dialog_action_set_refresh (pda, refresh);
594 psppire_dialog_action_set_valid_predicate (pda, dialog_state_valid);
600 psppire_dialog_action_aggregate_class_init (PsppireDialogActionAggregateClass *class)
602 psppire_dialog_action_set_activation (class, psppire_dialog_action_aggregate_activate);
603 PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax;
608 psppire_dialog_action_aggregate_init (PsppireDialogActionAggregate *act)
613 /* Append the syntax of the summary function pointed to by ITER to STRING */
615 append_summary_spec (const PsppireDialogActionAggregate *agg, GtkTreeIter *iter, GString *string)
617 GtkTreeIter combo_iter;
618 char *varname = NULL;
619 char *funcname = NULL;
621 GtkTreeModel *acr_model = GTK_TREE_MODEL (PSPPIRE_ACR (agg->summary_acr)->list_store);
622 GtkTreeModel *combo_model = gtk_combo_box_get_model (GTK_COMBO_BOX (agg->function_combo));
625 /* This is an index into the combo_model. Its used to get the function name */
629 enum agr_src_vars has_src_vars;
631 gchar *srcvar = NULL;
633 gtk_tree_model_get (acr_model, iter,
634 SUMMARY_COL_VARNAME, &varname,
635 SUMMARY_COL_VARLABEL, &label,
636 SUMMARY_COL_FUNCIDX, &f_idx,
637 SUMMARY_COL_SRCVAR, &srcvar,
638 SUMMARY_COL_ARG1, &arg1,
639 SUMMARY_COL_ARG2, &arg2,
642 gtk_tree_model_iter_nth_child (combo_model, &combo_iter, NULL, f_idx);
644 gtk_tree_model_get (combo_model, &combo_iter,
645 COMBO_MODEL_COL_SYNTAX, &funcname,
646 COMBO_MODEL_COL_ARITY, &arity,
647 COMBO_MODEL_COL_SRC_VARS, &has_src_vars,
650 g_string_append (string, varname);
652 if (0 != strcmp ("", label))
656 syntax_gen_string (&ss, ss_cstr (label));
657 g_string_append (string, " ");
658 g_string_append (string, ds_cstr (&ss));
662 g_string_append_printf (string, " = %s", funcname);
664 if ( has_src_vars != AGR_SV_NO)
667 ds_init_cstr (&dss, " (");
669 ds_put_cstr (&dss, srcvar);
672 ds_put_c_format (&dss, ", %.*g", DBL_DIG + 1, arg1);
675 ds_put_c_format (&dss, ", %.*g", DBL_DIG + 1, arg2);
677 ds_put_cstr (&dss, ")");
679 g_string_append (string, ds_cstr (&dss));