Enables Data Open/Save/New menuitems.
[pspp-builds.git] / src / ui / gui / data-editor.c
index abf84fad9a13deb2e3a1bdcd637927de1e9894ce..9aa6501be01a9d54b15746b9adf3415beb008543 100644 (file)
@@ -1,6 +1,6 @@
 /*
     PSPPIRE --- A Graphical User Interface for PSPP
-    Copyright (C) 2006  Free Software Foundation
+    Copyright (C) 2006, 2007  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
 
 #include "helper.h"
 #include "about.h"
+#include "psppire-dialog.h"
+#include "psppire-var-select.h"
 
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
 #include "data-editor.h"
 #include "syntax-editor.h"
+#include <language/syntax-string-source.h>
 #include "window-manager.h"
 
 #include "psppire-data-store.h"
 #include "psppire-var-store.h"
 
+#include "weight-cases-dialog.h"
+
+static void register_data_editor_actions (struct data_editor *de);
 
 static void insert_variable (GtkCheckMenuItem *m, gpointer data);
 
@@ -55,6 +61,11 @@ static gboolean click2row (GtkWidget *w, gint row, gpointer data);
 static void select_sheet (struct data_editor *de, guint page_num);
 
 
+/* Callback for when the dictionary changes properties*/
+static void on_weight_change (GObject *, gint, gpointer);
+static void on_filter_change (GObject *, gint, gpointer);
+static void on_split_change (PsppireDict *, gpointer);
+
 static void data_var_select (GtkNotebook *notebook,
                            GtkNotebookPage *page,
                            guint page_num,
@@ -100,6 +111,7 @@ disable_edit_clear (GtkWidget *w, gint x, gint y, gpointer data)
   return FALSE;
 }
 
+static void weight_cases_dialog (GObject *o, gpointer data);
 
 
 /*
@@ -110,26 +122,58 @@ new_data_editor (void)
 {
   struct data_editor *de ;
   struct editor_window *e;
+  GtkSheet *var_sheet ;
+  PsppireVarStore *vs;
 
-  de = g_malloc (sizeof (*de));
+  de = g_malloc0 (sizeof (*de));
 
   e = (struct editor_window *) de;
 
   de->xml = glade_xml_new (PKGDATADIR "/data-editor.glade", NULL, NULL);
 
+
+  var_sheet = GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
+
+  vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
+
+  g_signal_connect (vs->dict, "weight-changed",
+                   G_CALLBACK (on_weight_change),
+                   de);
+
+  g_signal_connect (vs->dict, "filter-changed",
+                   G_CALLBACK (on_filter_change),
+                   de);
+
+  g_signal_connect (vs->dict, "split-changed",
+                   G_CALLBACK (on_split_change),
+                   de);
+
   connect_help (de->xml);
 
+
+  register_data_editor_actions (de);
+
+  de->invoke_weight_cases_dialog =
+    gtk_action_new ("weight-cases-dialog",
+                   _("Weights"),
+                   _("Weight cases by variable"),
+                   "pspp-weight-cases");
+
+
+  g_signal_connect (de->invoke_weight_cases_dialog, "activate",
+                   G_CALLBACK (weight_cases_dialog), de);
+
   e->window = GTK_WINDOW (get_widget_assert (de->xml, "data_editor"));
 
-  g_signal_connect (get_widget_assert (de->xml,"file_new_data"),
-                   "activate",
-                   G_CALLBACK (new_data_window),
-                   e->window);
+  g_signal_connect_swapped (get_widget_assert (de->xml,"file_new_data"),
+                           "activate",
+                           G_CALLBACK (gtk_action_activate),
+                           de->action_data_new);
 
-  g_signal_connect (get_widget_assert (de->xml,"file_open_data"),
-                   "activate",
-                   G_CALLBACK (open_data_window),
-                   e->window);
+  g_signal_connect_swapped (get_widget_assert (de->xml,"file_open_data"),
+                           "activate",
+                           G_CALLBACK (gtk_action_activate),
+                           de->action_data_open);
 
   g_signal_connect (get_widget_assert (de->xml,"file_new_syntax"),
                    "activate",
@@ -141,6 +185,16 @@ new_data_editor (void)
                    G_CALLBACK (open_syntax_window),
                    e->window);
 
+  g_signal_connect_swapped (get_widget_assert (de->xml,"file_save"),
+                           "activate",
+                           G_CALLBACK (gtk_action_activate),
+                           de->action_data_save);
+
+  g_signal_connect_swapped (get_widget_assert (de->xml,"file_save_as"),
+                           "activate",
+                           G_CALLBACK (gtk_action_activate),
+                           de->action_data_save_as);
+
 
   g_signal_connect (get_widget_assert (de->xml,"edit_clear"),
                    "activate",
@@ -153,6 +207,10 @@ new_data_editor (void)
                    G_CALLBACK (insert_variable),
                    de);
 
+  gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
+                           get_widget_assert (de->xml, "data_weight-cases")
+                           );
+
 
   g_signal_connect (get_widget_assert (de->xml,"help_about"),
                    "activate",
@@ -232,6 +290,17 @@ new_data_editor (void)
                    "toggled",
                    G_CALLBACK (value_labels_toggled), de);
 
+  gtk_action_connect_proxy (de->action_data_open,
+                           get_widget_assert (de->xml, "button-open")
+                           );
+
+  gtk_action_connect_proxy (de->action_data_save,
+                           get_widget_assert (de->xml, "button-save")
+                           );
+
+  gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
+                           get_widget_assert (de->xml, "button-weight-cases")
+                           );
 
   g_signal_connect (get_widget_assert (de->xml, "file_quit"),
                    "activate",
@@ -242,6 +311,7 @@ new_data_editor (void)
                    "activate",
                    G_CALLBACK (minimise_all_windows), NULL);
 
+
   select_sheet (de, PAGE_DATA_SHEET);
 
   return de;
@@ -292,8 +362,6 @@ click2column (GtkWidget *w, gint col, gpointer data)
 }
 
 
-
-
 void
 new_data_window (GtkMenuItem *menuitem, gpointer parent)
 {
@@ -356,59 +424,6 @@ data_editor_select_sheet (struct data_editor *de, gint page)
 }
 
 
-void
-open_data_window (GtkMenuItem *menuitem, gpointer parent)
-{
-  bool finished = FALSE;
-
-  GtkWidget *dialog;
-
-  GtkFileFilter *filter ;
-
-  dialog = gtk_file_chooser_dialog_new (_("Open"),
-                                       GTK_WINDOW (parent),
-                                       GTK_FILE_CHOOSER_ACTION_OPEN,
-                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-                                       NULL);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
-  gtk_file_filter_add_pattern (filter, "*.sav");
-  gtk_file_filter_add_pattern (filter, "*.SAV");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
-  gtk_file_filter_add_pattern (filter, "*.por");
-  gtk_file_filter_add_pattern (filter, "*.POR");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  filter = gtk_file_filter_new ();
-  gtk_file_filter_set_name (filter, _("All Files"));
-  gtk_file_filter_add_pattern (filter, "*");
-  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
-
-  do {
-
-    if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
-      {
-       gchar *file_name =
-         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
-
-       g_free (file_name);
-      }
-    else
-      finished = TRUE;
-
-  } while ( ! finished ) ;
-
-  gtk_widget_destroy (dialog);
-}
-
-
-
-
 static void
 status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
 {
@@ -422,8 +437,6 @@ status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
 }
 
 
-
-
 static void
 grid_lines_activate (GtkCheckMenuItem *menuitem, gpointer data)
 {
@@ -602,12 +615,9 @@ insert_variable (GtkCheckMenuItem *m, gpointer data)
   GtkSheet *var_sheet =
     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
 
-
-
   PsppireVarStore *vs = PSPPIRE_VAR_STORE
     (gtk_sheet_get_model (var_sheet) );
 
-
   switch ( gtk_notebook_get_current_page ( GTK_NOTEBOOK (notebook)) )
     {
     case PAGE_VAR_SHEET:
@@ -629,7 +639,451 @@ insert_variable (GtkCheckMenuItem *m, gpointer data)
     }
 
   psppire_dict_insert_variable (vs->dict, posn, NULL);
+}
+
+/* Callback for when the dictionary changes its split variables */
+static void
+on_split_change (PsppireDict *dict, gpointer data)
+{
+  struct data_editor *de = data;
+
+  size_t n_split_vars = dict_get_split_cnt (dict->dict);
+
+  GtkWidget *split_status_area =
+    get_widget_assert (de->xml, "split-file-status-area");
+
+  if ( n_split_vars == 0 )
+    {
+      gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
+    }
+  else
+    {
+      gint i;
+      GString *text;
+      struct variable *const * split_vars = dict_get_split_vars (dict->dict);
+
+      text = g_string_new (_("Split by "));
+
+      for (i = 0 ; i < n_split_vars - 1; ++i )
+       {
+         g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
+       }
+      g_string_append (text, var_get_name (split_vars[i]));
+
+      gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
+
+      g_string_free (text, TRUE);
+    }
+}
+
+
+/* Callback for when the dictionary changes its filter variable */
+static void
+on_filter_change (GObject *o, gint filter_index, gpointer data)
+{
+  struct data_editor *de = data;
+  GtkWidget *filter_status_area =
+    get_widget_assert (de->xml, "filter-use-status-area");
+
+  if ( filter_index == -1 )
+    {
+      gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
+    }
+  else
+    {
+      GtkSheet *var_sheet =
+       GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
+
+      PsppireVarStore *vs = PSPPIRE_VAR_STORE
+       (gtk_sheet_get_model (var_sheet) );
+
+      struct variable *var = psppire_dict_get_variable (vs->dict,
+                                                       filter_index);
+
+      gchar *text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
+
+      gtk_label_set_text (GTK_LABEL (filter_status_area), text);
+
+      g_free (text);
+    }
+}
+
+/* Callback for when the dictionary changes its weights */
+static void
+on_weight_change (GObject *o, gint weight_index, gpointer data)
+{
+  struct data_editor *de = data;
+  GtkWidget *weight_status_area =
+    get_widget_assert (de->xml, "weight-status-area");
+
+  if ( weight_index == -1 )
+    {
+      gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
+    }
+  else
+    {
+      GtkSheet *var_sheet =
+       GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
+
+      PsppireVarStore *vs = PSPPIRE_VAR_STORE
+       (gtk_sheet_get_model (var_sheet) );
+
+      struct variable *var = psppire_dict_get_variable (vs->dict,
+                                                       weight_index);
+
+      gchar *text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
+
+      gtk_label_set_text (GTK_LABEL (weight_status_area), text);
+
+      g_free (text);
+    }
+}
+
+static void
+weight_cases_dialog (GObject *o, gpointer data)
+{
+  gint response;
+  struct data_editor *de = data;
+  GtkSheet *var_sheet =
+    GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
+
+
+  GladeXML *xml = glade_xml_new (PKGDATADIR "/psppire.glade",
+                                "weight-cases-dialog", NULL);
+
+
+  GtkWidget *treeview =  get_widget_assert (xml, "treeview");
+  GtkWidget *entry =  get_widget_assert (xml, "entry1");
+
+
+  PsppireVarStore *vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
+
+  PsppireVarSelect *select = psppire_var_select_new (treeview,
+                                                    entry, vs->dict);
+
+
+  PsppireDialog *dialog = create_weight_dialog (select, xml);
+
+  response = psppire_dialog_run (dialog);
+
+  g_object_unref (xml);
+
+  switch (response)
+    {
+    case GTK_RESPONSE_OK:
+    {
+      struct getl_interface *sss ;
+      const GList *list = psppire_var_select_get_variables (select);
+
+      g_assert ( g_list_length ((GList *)list) <= 1 );
+
+      if ( list == NULL)
+         {
+           sss = create_syntax_string_source ("WEIGHT OFF.");
+         }
+      else
+       {
+         struct variable *var = list->data;
+
+           sss = create_syntax_string_source ("WEIGHT BY %s.\n",
+                                              var_get_name (var));
+         }
+
+       execute_syntax (sss);
+       }
+      break;
+    case PSPPIRE_RESPONSE_PASTE:
+      {
+       struct syntax_editor *se =  (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL);
+
+       const GList *list = psppire_var_select_get_variables (select);
+
+       g_assert ( g_list_length ((GList *)list) <= 1 );
+
+       if ( list == NULL)
+         {
+           gtk_text_buffer_insert_at_cursor (se->buffer, "WEIGHT OFF.", -1);
+         }
+       else
+         {
+           struct variable *var = list->data;
+
+           gchar *text = g_strdup_printf ("WEIGHT BY %s.",
+                                          var_get_name (var));
+
+           gtk_text_buffer_insert_at_cursor (se->buffer,
+                                             text, -1);
+
+           g_free (text);
+         }
+      }
+      break;
+    default:
+      break;
+    }
+}
+
+
+
+\f
+static void data_save_as_dialog (GtkAction *, struct data_editor *de);
+static void new_file (GtkAction *, struct editor_window *de);
+static void open_data_dialog (GtkAction *, struct editor_window *de);
+static void data_save (GtkAction *action, struct data_editor *e);
+
+
+/* Create the GtkActions and connect to their signals */
+static void
+register_data_editor_actions (struct data_editor *de)
+{
+  de->action_data_open =
+    gtk_action_new ("data-open-dialog",
+                   _("Open"),
+                   _("Open a data file"),
+                   "gtk-open");
+
+  g_signal_connect (de->action_data_open, "activate",
+                   G_CALLBACK (open_data_dialog), de);
+
+
+  de->action_data_save = gtk_action_new ("data-save",
+                                           _("Save"),
+                                           _("Save data to file"),
+                                           "gtk-save");
+
+  g_signal_connect (de->action_data_save, "activate",
+                   G_CALLBACK (data_save), de);
+
+
+
+  de->action_data_save_as = gtk_action_new ("data-save-as-dialog",
+                                           _("Save As"),
+                                           _("Save data to file"),
+                                           "gtk-save");
+
+  g_signal_connect (de->action_data_save_as, "activate",
+                   G_CALLBACK (data_save_as_dialog), de);
+
+  de->action_data_new =
+    gtk_action_new ("data-new",
+                   _("New"),
+                   _("New data file"),
+                   NULL);
+
+  g_signal_connect (de->action_data_new, "activate",
+                   G_CALLBACK (new_file), de);
+}
+
+/* Returns true if NAME has a suffix which might denote a PSPP file */
+static gboolean
+name_has_suffix (const gchar *name)
+{
+  if ( g_str_has_suffix (name, ".sav"))
+    return TRUE;
+  if ( g_str_has_suffix (name, ".SAV"))
+    return TRUE;
+  if ( g_str_has_suffix (name, ".por"))
+    return TRUE;
+  if ( g_str_has_suffix (name, ".POR"))
+    return TRUE;
+
+  return FALSE;
+}
+
+/* Append SUFFIX to the filename of DE */
+static void
+append_filename_suffix (struct data_editor *de, const gchar *suffix)
+{
+  if ( ! name_has_suffix (de->file_name))
+    {
+      gchar *s = de->file_name;
+      de->file_name = g_strconcat (de->file_name, suffix, NULL);
+      g_free (s);
+    }
+}
+
+/* Save DE to file */
+static void
+save_file (struct data_editor *de)
+{
+  struct getl_interface *sss;
+
+  g_assert (de->file_name);
+
+  if ( de->save_as_portable )
+    {
+      append_filename_suffix (de, ".por");
+      sss = create_syntax_string_source ("EXPORT OUTFILE='%s'.",
+                                        de->file_name);
+    }
+  else
+    {
+      append_filename_suffix (de, ".sav");
+      sss = create_syntax_string_source ("SAVE OUTFILE='%s'.",
+                                        de->file_name);
+    }
+
+  execute_syntax (sss);
+}
+
+
+/* Callback for data_save action.
+   If there's an existing file name, then just save,
+   otherwise prompt for a file name, then save */
+static void
+data_save (GtkAction *action, struct data_editor *de)
+{
+  if ( de->file_name)
+    save_file (de);
+  else
+    data_save_as_dialog (action, de);
+}
+
+
+/* Callback for data_save_as action. Prompt for a filename and save */
+static void
+data_save_as_dialog (GtkAction *action, struct data_editor *de)
+{
+  struct editor_window *e = (struct editor_window *) de;
+
+  GtkWidget *button_sys;
+  GtkWidget *dialog =
+    gtk_file_chooser_dialog_new (_("Save"),
+                                GTK_WINDOW (e->window),
+                                GTK_FILE_CHOOSER_ACTION_SAVE,
+                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+                                NULL);
+
+  GtkFileFilter *filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
+  gtk_file_filter_add_pattern (filter, "*.sav");
+  gtk_file_filter_add_pattern (filter, "*.SAV");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
+  gtk_file_filter_add_pattern (filter, "*.por");
+  gtk_file_filter_add_pattern (filter, "*.POR");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("All Files"));
+  gtk_file_filter_add_pattern (filter, "*");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  {
+    GtkWidget *button_por;
+    GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
+    button_sys =
+      gtk_radio_button_new_with_label (NULL, _("System File"));
+
+    button_por =
+      gtk_radio_button_new_with_label
+      (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
+       _("Portable File"));
+
+    gtk_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
+    gtk_box_pack_start_defaults (GTK_BOX (vbox), button_por);
+
+    gtk_widget_show_all (vbox);
+
+    gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
+  }
+
+  switch (gtk_dialog_run (GTK_DIALOG (dialog)))
+    {
+    case GTK_RESPONSE_ACCEPT:
+      {
+       g_free (de->file_name);
+
+       de->file_name =
+         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
 
+       de->save_as_portable =
+         ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
+
+       save_file (de);
+
+       window_set_name_from_filename (e, de->file_name);
+      }
+      break;
+    default:
+      break;
+    }
+
+  gtk_widget_destroy (dialog);
 }
 
 
+/* Callback for data_new action.
+   Performs the NEW FILE command */
+static void
+new_file (GtkAction *action, struct editor_window *de)
+{
+  struct getl_interface *sss =
+    create_syntax_string_source ("NEW FILE.");
+
+  execute_syntax (sss);
+
+  default_window_name (de);
+}
+
+
+/* Callback for the data_open action.
+   Prompts for a filename and opens it */
+static void
+open_data_dialog (GtkAction *action, struct editor_window *de)
+{
+  GtkWidget *dialog =
+    gtk_file_chooser_dialog_new (_("Open"),
+                                GTK_WINDOW (de->window),
+                                GTK_FILE_CHOOSER_ACTION_OPEN,
+                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                                NULL);
+
+  GtkFileFilter *filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
+  gtk_file_filter_add_pattern (filter, "*.sav");
+  gtk_file_filter_add_pattern (filter, "*.SAV");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
+  gtk_file_filter_add_pattern (filter, "*.por");
+  gtk_file_filter_add_pattern (filter, "*.POR");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_set_name (filter, _("All Files"));
+  gtk_file_filter_add_pattern (filter, "*");
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+  switch (gtk_dialog_run (GTK_DIALOG (dialog)))
+    {
+    case GTK_RESPONSE_ACCEPT:
+      {
+       struct getl_interface *sss;
+       gchar *file_name =
+         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+       sss = create_syntax_string_source ("GET FILE='%s'.", file_name);
+
+       execute_syntax (sss);
+
+       window_set_name_from_filename (de, file_name);
+
+       g_free (file_name);
+      }
+      break;
+    default:
+      break;
+    }
+
+  gtk_widget_destroy (dialog);
+}
+
+
+
+