gui: Add charts, additional formatting feature to FREQUENCIES dialog.
[pspp] / src / ui / gui / frequencies-dialog.c
index c78fa95f1869bd907946af25c879803c605933c2..e812d9a3f1a55b18c4bd6ad53ac1d4adf14982b9 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2007  Free Software Foundation
+   Copyright (C) 2007, 2010  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,6 +18,7 @@
 
 #include "checkbox-treeview.h"
 #include "frequencies-dialog.h"
+#include "psppire-var-view.h"
 
 #include <gtk/gtk.h>
 #include <stdlib.h>
@@ -29,7 +30,7 @@
 #include <ui/gui/helper.h>
 #include <ui/gui/psppire-dialog.h>
 #include <ui/gui/psppire-var-store.h>
-#include <ui/gui/helper.h>
+#include "executor.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -87,23 +88,59 @@ enum frq_order
     FRQ_DCOUNT
   };
 
-struct format_options
+enum frq_table
+  {
+    FRQ_TABLE,
+    FRQ_NOTABLE,
+    FRQ_LIMIT
+  };
+
+struct tables_options
 {
   enum frq_order order;
-  gboolean use_limits;
+  enum frq_table table;
   int limit;
 };
 
+enum frq_scale
+  {
+    FRQ_FREQ,
+    FRQ_PERCENT
+  };
+
+struct charts_options
+  {
+    bool use_min;
+    double min;
+    bool use_max;
+    double max;
+    bool draw_hist;
+    bool draw_normal;
+    enum frq_scale scale;
+    bool draw_pie;
+    bool pie_include_missing;
+  };
+
 struct frequencies_dialog
 {
+  /* Main dialog. */
   GtkTreeView *stat_vars;
   PsppireDict *dict;
 
-  GtkWidget *table_button;
+  GtkWidget *tables_button;
+  GtkWidget *charts_button;
 
-  GtkWidget *format_dialog;
-  GtkWidget *maximum_cats;
-  GtkWidget *limit_toggle_button;
+  GtkToggleButton *include_missing;
+
+  GtkTreeModel *stats;
+
+  /* Frequency Tables dialog. */
+  GtkWidget *tables_dialog;
+  struct tables_options tables_opts;
+
+  GtkToggleButton *always;
+  GtkToggleButton *never;
+  GtkToggleButton *limit;
   GtkSpinButton *limit_spinbutton;
 
   GtkToggleButton  *avalue;
@@ -111,9 +148,23 @@ struct frequencies_dialog
   GtkToggleButton  *afreq;
   GtkToggleButton  *dfreq;
 
-  struct format_options current_opts;
+  /* Charts dialog. */
+  GtkWidget *charts_dialog;
+  struct charts_options charts_opts;
 
-  GtkTreeModel *stats;
+  GtkToggleButton *freqs;
+  GtkToggleButton *percents;
+
+  GtkToggleButton *min;
+  GtkSpinButton *min_spin;
+  GtkToggleButton *max;
+  GtkSpinButton *max_spin;
+
+  GtkToggleButton *hist;
+  GtkToggleButton *normal;
+
+  GtkToggleButton *pie;
+  GtkToggleButton *pie_include_missing;
 };
 
 static void
@@ -131,8 +182,6 @@ refresh (PsppireDialog *dialog, struct frequencies_dialog *fd)
     gtk_list_store_set (GTK_LIST_STORE (fd->stats), &iter,
                        CHECKBOX_COLUMN_SELECTED,
                         (B_FS_DEFAULT & (1u << i)) ? true : false, -1);
-
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->table_button), TRUE);
 }
 
 static char *
@@ -147,11 +196,11 @@ generate_syntax (const struct frequencies_dialog *fd)
   GString *string = g_string_new ("FREQUENCIES");
 
   g_string_append (string, "\n\t/VARIABLES=");
-  append_variable_names (string, fd->dict, GTK_TREE_VIEW (fd->stat_vars), 0);
+  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (fd->stat_vars), 0, string);
 
   g_string_append (string, "\n\t/FORMAT=");
 
-  switch (fd->current_opts.order)
+  switch (fd->tables_opts.order)
     {
     case FRQ_AVALUE:
       g_string_append (string, "AVALUE");
@@ -171,16 +220,17 @@ generate_syntax (const struct frequencies_dialog *fd)
 
   g_string_append (string, " ");
 
-  if ( fd->current_opts.use_limits )
-    {
-      g_string_append_printf (string, "LIMIT (%d)", fd->current_opts.limit);
-    }
-  else
+  switch (fd->tables_opts.table)
     {
-      if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->table_button)))
-       g_string_append (string, "TABLE");
-      else
-       g_string_append (string, "NOTABLE");
+    case FRQ_TABLE:
+      g_string_append (string, "TABLE");
+      break;
+    case FRQ_NOTABLE:
+      g_string_append (string, "NOTABLE");
+      break;
+    case FRQ_LIMIT:
+      g_string_append_printf (string, "LIMIT (%d)", fd->tables_opts.limit);
+      break;
     }
 
 
@@ -220,6 +270,37 @@ generate_syntax (const struct frequencies_dialog *fd)
         }
     }
 
+  if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->include_missing)))
+    g_string_append (string, "\n\t/MISSING=INCLUDE");
+
+  if (fd->charts_opts.draw_hist)
+    {
+      g_string_append (string, "\n\t/HISTOGRAM=");
+      g_string_append (string,
+                       fd->charts_opts.draw_normal ? "NORMAL" : "NONORMAL");
+
+      if (fd->charts_opts.scale == FRQ_PERCENT)
+        g_string_append (string, " PERCENT");
+
+      if (fd->charts_opts.use_min)
+        g_string_append_printf (string, " MIN(%.15g)", fd->charts_opts.min);
+      if (fd->charts_opts.use_max)
+        g_string_append_printf (string, " MAX(%.15g)", fd->charts_opts.max);
+    }
+
+  if (fd->charts_opts.draw_pie)
+    {
+      g_string_append (string, "\n\t/PIECHART=");
+
+      if (fd->charts_opts.pie_include_missing)
+        g_string_append (string, " MISSING");
+
+      if (fd->charts_opts.use_min)
+        g_string_append_printf (string, " MIN(%.15g)", fd->charts_opts.min);
+      if (fd->charts_opts.use_max)
+        g_string_append_printf (string, " MAX(%.15g)", fd->charts_opts.max);
+    }
+
   g_string_append (string, ".\n");
 
   text = string->str;
@@ -244,12 +325,11 @@ dialog_state_valid (gpointer data)
 
 
 static void
-on_format_clicked (struct frequencies_dialog *fd)
+on_tables_clicked (struct frequencies_dialog *fd)
 {
   int ret;
-  g_signal_emit_by_name (fd->limit_toggle_button, "toggled");
 
-  switch (fd->current_opts.order)
+  switch (fd->tables_opts.order)
     {
     case FRQ_AVALUE:
       gtk_toggle_button_set_active (fd->avalue, TRUE);
@@ -258,37 +338,106 @@ on_format_clicked (struct frequencies_dialog *fd)
       gtk_toggle_button_set_active (fd->dvalue, TRUE);
       break;
     case FRQ_ACOUNT:
-      gtk_toggle_button_set_active (fd->dfreq, TRUE);
+      gtk_toggle_button_set_active (fd->afreq, TRUE);
       break;
     case FRQ_DCOUNT:
       gtk_toggle_button_set_active (fd->dfreq, TRUE);
       break;
     };
 
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fd->limit_toggle_button),
-                               fd->current_opts.use_limits);
-
+  switch (fd->tables_opts.table)
+    {
+    case FRQ_TABLE:
+      gtk_toggle_button_set_active (fd->always, TRUE);
+      break;
+    case FRQ_NOTABLE:
+      gtk_toggle_button_set_active (fd->never, TRUE);
+      break;
+    case FRQ_LIMIT:
+      gtk_toggle_button_set_active (fd->limit, TRUE);
+      break;
+    }
   gtk_spin_button_set_value (fd->limit_spinbutton,
-                            fd->current_opts.limit);
+                            fd->tables_opts.limit);
+  g_signal_emit_by_name (fd->limit, "toggled");
 
-  ret = psppire_dialog_run (PSPPIRE_DIALOG (fd->format_dialog));
+  ret = psppire_dialog_run (PSPPIRE_DIALOG (fd->tables_dialog));
 
   if ( ret == PSPPIRE_RESPONSE_CONTINUE )
     {
       if (gtk_toggle_button_get_active (fd->avalue))
-       fd->current_opts.order = FRQ_AVALUE;
+       fd->tables_opts.order = FRQ_AVALUE;
       else if (gtk_toggle_button_get_active (fd->dvalue))
-       fd->current_opts.order = FRQ_DVALUE;
+       fd->tables_opts.order = FRQ_DVALUE;
       else if (gtk_toggle_button_get_active (fd->afreq))
-       fd->current_opts.order = FRQ_ACOUNT;
+       fd->tables_opts.order = FRQ_ACOUNT;
       else if (gtk_toggle_button_get_active (fd->dfreq))
-       fd->current_opts.order = FRQ_DCOUNT;
+       fd->tables_opts.order = FRQ_DCOUNT;
 
-      fd->current_opts.use_limits = gtk_toggle_button_get_active
-       (GTK_TOGGLE_BUTTON (fd->limit_toggle_button));
+      if (gtk_toggle_button_get_active (fd->always))
+        fd->tables_opts.table = FRQ_TABLE;
+      else if (gtk_toggle_button_get_active (fd->never))
+        fd->tables_opts.table = FRQ_NOTABLE;
+      else
+        fd->tables_opts.table = FRQ_LIMIT;
 
-      fd->current_opts.limit =
-       gtk_spin_button_get_value (fd->limit_spinbutton);
+      fd->tables_opts.limit = gtk_spin_button_get_value (fd->limit_spinbutton);
+    }
+}
+
+static void
+on_charts_clicked (struct frequencies_dialog *fd)
+{
+  int ret;
+
+  gtk_toggle_button_set_active (fd->min, fd->charts_opts.use_min);
+  gtk_spin_button_set_value (fd->min_spin, fd->charts_opts.min);
+  g_signal_emit_by_name (fd->min, "toggled");
+
+  gtk_toggle_button_set_active (fd->max, fd->charts_opts.use_max);
+  gtk_spin_button_set_value (fd->max_spin, fd->charts_opts.max);
+  g_signal_emit_by_name (fd->max, "toggled");
+
+  gtk_toggle_button_set_active (fd->hist, fd->charts_opts.draw_hist);
+  gtk_toggle_button_set_active (fd->normal, fd->charts_opts.draw_normal);
+  g_signal_emit_by_name (fd->hist, "toggled");
+
+  switch (fd->charts_opts.scale)
+    {
+    case FRQ_FREQ:
+      gtk_toggle_button_set_active (fd->freqs, TRUE);
+      break;
+    case FRQ_DVALUE:
+      gtk_toggle_button_set_active (fd->percents, TRUE);
+      break;
+    };
+
+
+  gtk_toggle_button_set_active (fd->pie, fd->charts_opts.draw_pie);
+  gtk_toggle_button_set_active (fd->pie_include_missing,
+                                fd->charts_opts.pie_include_missing);
+  g_signal_emit_by_name (fd->pie, "toggled");
+
+  ret = psppire_dialog_run (PSPPIRE_DIALOG (fd->charts_dialog));
+
+  if ( ret == PSPPIRE_RESPONSE_CONTINUE )
+    {
+      fd->charts_opts.use_min = gtk_toggle_button_get_active (fd->min);
+      fd->charts_opts.min = gtk_spin_button_get_value (fd->min_spin);
+
+      fd->charts_opts.use_max = gtk_toggle_button_get_active (fd->max);
+      fd->charts_opts.max = gtk_spin_button_get_value (fd->max_spin);
+
+      fd->charts_opts.draw_hist = gtk_toggle_button_get_active (fd->hist);
+      fd->charts_opts.draw_normal = gtk_toggle_button_get_active (fd->normal);
+      if (gtk_toggle_button_get_active (fd->freqs))
+       fd->charts_opts.scale = FRQ_FREQ;
+      else if (gtk_toggle_button_get_active (fd->percents))
+       fd->charts_opts.scale = FRQ_PERCENT;
+
+      fd->charts_opts.draw_pie = gtk_toggle_button_get_active (fd->pie);
+      fd->charts_opts.pie_include_missing
+        = gtk_toggle_button_get_active (fd->pie_include_missing);
     }
 }
 
@@ -316,8 +465,8 @@ frequencies_dialog (GObject *o, gpointer data)
   GtkWidget *dialog = get_widget_assert   (xml, "frequencies-dialog");
   GtkWidget *source = get_widget_assert   (xml, "dict-treeview");
   GtkWidget *dest =   get_widget_assert   (xml, "var-treeview");
-  GtkWidget *selector = get_widget_assert (xml, "selector1");
-  GtkWidget *format_button = get_widget_assert (xml, "button1");
+  GtkWidget *tables_button = get_widget_assert (xml, "tables-button");
+  GtkWidget *charts_button = get_widget_assert (xml, "charts-button");
   GtkWidget *stats_treeview = get_widget_assert (xml, "stats-treeview");
 
   PsppireVarStore *vs = NULL;
@@ -333,55 +482,88 @@ frequencies_dialog (GObject *o, gpointer data)
 
   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (de));
 
-  g_object_set (source, "dictionary", vs->dict, NULL);
-
-  set_dest_model (GTK_TREE_VIEW (dest), vs->dict);
-
-
-  psppire_selector_set_subjects (PSPPIRE_SELECTOR (selector),
-                                source,
-                                dest,
-                                insert_source_row_into_tree_view,
-                                NULL,
-                                NULL);
-
+  g_object_get (vs, "dictionary", &fd.dict, NULL);
+  g_object_set (source, "model", fd.dict, NULL);
 
   fd.stat_vars = GTK_TREE_VIEW (dest);
-  fd.dict = vs->dict;
-  fd.table_button = get_widget_assert (xml, "checkbutton1");
-  fd.format_dialog = get_widget_assert (xml, "format-dialog");
-  fd.maximum_cats = get_widget_assert (xml, "hbox5");
-  fd.limit_toggle_button = get_widget_assert (xml, "checkbutton2");
-  fd.limit_spinbutton =
-    GTK_SPIN_BUTTON (get_widget_assert (xml, "spinbutton1"));
+  fd.tables_button = get_widget_assert (xml, "tables-button");
+  fd.charts_button = get_widget_assert (xml, "charts-button");
 
-  fd.stats = gtk_tree_view_get_model (GTK_TREE_VIEW (stats_treeview));
-
-  fd.avalue = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "radiobutton1"));
-  fd.dvalue = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "radiobutton2"));
-  fd.afreq  = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "radiobutton3"));
-  fd.dfreq  = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "radiobutton4"));
-
-  fd.current_opts.order = FRQ_AVALUE;
-  fd.current_opts.use_limits = FALSE;
-  fd.current_opts.limit = 50;
+  fd.include_missing = GTK_TOGGLE_BUTTON (
+    get_widget_assert (xml, "include_missing"));
 
+  fd.stats = gtk_tree_view_get_model (GTK_TREE_VIEW (stats_treeview));
 
-  gtk_window_set_transient_for (GTK_WINDOW (fd.format_dialog), GTK_WINDOW (de));
-
+  /* Frequency Tables dialog. */
+  fd.tables_dialog = get_widget_assert (xml, "tables-dialog");
+  fd.tables_opts.order = FRQ_AVALUE;
+  fd.tables_opts.table = FRQ_TABLE;
+  fd.tables_opts.limit = 50;
 
+  fd.always = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "always"));
+  fd.never = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "never"));
+  fd.limit  = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "limit"));
+  fd.limit_spinbutton =
+    GTK_SPIN_BUTTON (get_widget_assert (xml, "limit-spin"));
+  g_signal_connect (fd.limit, "toggled",
+                   G_CALLBACK (sensitive_if_active), fd.limit_spinbutton);
+
+  fd.avalue = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "avalue"));
+  fd.dvalue = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "dvalue"));
+  fd.afreq  = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "afreq"));
+  fd.dfreq  = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "dfreq"));
+
+  gtk_window_set_transient_for (GTK_WINDOW (fd.tables_dialog),
+                                GTK_WINDOW (de));
+
+  /* Charts dialog. */
+  fd.charts_dialog = get_widget_assert (xml, "charts-dialog");
+  fd.charts_opts.use_min = false;
+  fd.charts_opts.min = 0;
+  fd.charts_opts.use_max = false;
+  fd.charts_opts.max = 100;
+  fd.charts_opts.draw_hist = false;
+  fd.charts_opts.draw_normal = false;
+  fd.charts_opts.scale = FRQ_FREQ;
+  fd.charts_opts.draw_pie = false;
+  fd.charts_opts.pie_include_missing = false;
+
+  fd.freqs = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "freqs"));
+  fd.percents = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "percents"));
+
+  fd.min = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "min"));
+  fd.min_spin = GTK_SPIN_BUTTON (get_widget_assert (xml, "min-spin"));
+  g_signal_connect (fd.min, "toggled",
+                   G_CALLBACK (sensitive_if_active), fd.min_spin);
+  fd.max = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "max"));
+  fd.max_spin = GTK_SPIN_BUTTON (get_widget_assert (xml, "max-spin"));
+  g_signal_connect (fd.max, "toggled",
+                   G_CALLBACK (sensitive_if_active), fd.max_spin);
+
+  fd.hist = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "hist"));
+  fd.normal = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "normal"));
+  g_signal_connect (fd.hist, "toggled",
+                   G_CALLBACK (sensitive_if_active), fd.normal);
+
+  fd.pie = GTK_TOGGLE_BUTTON (get_widget_assert (xml, "pie"));
+  fd.pie_include_missing = GTK_TOGGLE_BUTTON (
+    get_widget_assert (xml, "pie-include-missing"));
+  g_signal_connect (fd.pie, "toggled",
+                   G_CALLBACK (sensitive_if_active), fd.pie_include_missing);
+
+  gtk_window_set_transient_for (GTK_WINDOW (fd.charts_dialog),
+                                GTK_WINDOW (de));
+
+  /* Main dialog. */
   g_signal_connect (dialog, "refresh", G_CALLBACK (refresh),  &fd);
 
   psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (dialog),
                                      dialog_state_valid, &fd);
 
-
-  g_signal_connect_swapped (format_button, "clicked",
-                           G_CALLBACK (on_format_clicked),  &fd);
-
-  g_signal_connect (fd.limit_toggle_button, "toggled",
-                   G_CALLBACK (sensitive_if_active), fd.maximum_cats);
-
+  g_signal_connect_swapped (tables_button, "clicked",
+                           G_CALLBACK (on_tables_clicked),  &fd);
+  g_signal_connect_swapped (charts_button, "clicked",
+                           G_CALLBACK (on_charts_clicked),  &fd);
 
   response = psppire_dialog_run (PSPPIRE_DIALOG (dialog));