Output Window: Fix sensitivity of Save button. Also add option for png and svg files.
[pspp] / src / ui / gui / psppire-output-window.c
index 14df40dbebc461fe17c05a1c338ac2bc211522ca..7a60cce3563fcea8bf360237333fef306af2cb44 100644 (file)
@@ -1,5 +1,6 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014  Free Software Foundation
+   Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016,
+   2021  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 "libpspp/cast.h"
 #include "libpspp/message.h"
 #include "libpspp/string-map.h"
-#include "output/cairo.h"
-#include "output/chart-item.h"
 #include "output/driver-provider.h"
-#include "output/message-item.h"
 #include "output/output-item.h"
-#include "output/tab.h"
-#include "output/table-item.h"
-#include "output/text-item.h"
 #include "ui/gui/help-menu.h"
 #include "ui/gui/builder-wrapper.h"
 #include "ui/gui/psppire-output-view.h"
+#include "ui/gui/psppire-conf.h"
+#include "ui/gui/windows-menu.h"
 
 #include "gl/xalloc.h"
 
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-static void psppire_output_window_class_init    (PsppireOutputWindowClass *class);
-static void psppire_output_window_init          (PsppireOutputWindow      *window);
-
-GType
-psppire_output_window_get_type (void)
-{
-  static GType psppire_output_window_type = 0;
-
-  if (!psppire_output_window_type)
-    {
-      static const GTypeInfo psppire_output_window_info =
-      {
-       sizeof (PsppireOutputWindowClass),
-       (GBaseInitFunc) NULL,
-        (GBaseFinalizeFunc) NULL,
-       (GClassInitFunc)psppire_output_window_class_init,
-       (GClassFinalizeFunc) NULL,
-       NULL,
-        sizeof (PsppireOutputWindow),
-       0,
-       (GInstanceInitFunc) psppire_output_window_init,
-      };
-
-      psppire_output_window_type =
-       g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireOutputWindow",
-                               &psppire_output_window_info, 0);
-    }
-
-  return psppire_output_window_type;
-}
+G_DEFINE_TYPE (PsppireOutputWindow, psppire_output_window, PSPPIRE_TYPE_WINDOW)
 
 static GObjectClass *parent_class;
 
@@ -94,7 +62,7 @@ psppire_output_window_dispose (GObject *obj)
 {
   PsppireOutputWindow *window = PSPPIRE_OUTPUT_WINDOW (obj);
 
-  if (window->dispose_has_run) 
+  if (window->dispose_has_run)
     return;
 
   window->dispose_has_run = TRUE;
@@ -112,7 +80,7 @@ psppire_output_window_class_init (PsppireOutputWindowClass *class)
 
   parent_class = g_type_class_peek_parent (class);
   object_class->dispose = psppire_output_window_dispose;
-  
+
   object_class->finalize = psppire_output_window_finalize;
 }
 \f
@@ -145,6 +113,10 @@ psppire_output_submit (struct output_driver *this,
   if (new)
     {
       pod->window = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
+      GApplication *app = g_application_get_default ();
+      gtk_application_add_window (GTK_APPLICATION (app),
+                                 GTK_WINDOW (pod->window));
+
       pod->window->driver = pod;
     }
   window = pod->window;
@@ -160,15 +132,35 @@ psppire_output_submit (struct output_driver *this,
       gtk_widget_show_all (GTK_WIDGET (pod->window));
     }
 
-  gtk_window_set_urgency_hint (GTK_WINDOW (pod->window), TRUE);
+  PsppireConf *conf = psppire_conf_new ();
+  {
+    gboolean status = true;
+    psppire_conf_get_boolean (conf, "OutputWindowAction", "alert",
+                             &status);
+    gtk_window_set_urgency_hint (GTK_WINDOW (pod->window), status);
+  }
+
+  {
+    gboolean status ;
+    if (psppire_conf_get_boolean (conf, "OutputWindowAction", "maximize",
+                                 &status) && status)
+      gtk_window_maximize (GTK_WINDOW (pod->window));
+  }
+
+  {
+    gboolean status ;
+    if (psppire_conf_get_boolean (conf, "OutputWindowAction", "raise",
+                                 &status) && status)
+      gtk_window_present (GTK_WINDOW (pod->window));
+  }
 }
 
 static struct output_driver_class psppire_output_class =
   {
-    "PSPPIRE",                  /* name */
-    NULL,                       /* destroy */
-    psppire_output_submit,      /* submit */
-    NULL,                       /* flush */
+    .name = "PSPPIRE",
+    .submit = psppire_output_submit,
+    .handles_groups = true,
+    .handles_show = true,
   };
 
 void
@@ -226,9 +218,10 @@ struct file_types
   const gchar *ext;
 };
 
-enum 
+enum
   {
     FT_AUTO = 0,
+    FT_SPV,
     FT_PDF,
     FT_HTML,
     FT_ODT,
@@ -236,20 +229,23 @@ enum
     FT_ASCII,
     FT_PS,
     FT_CSV,
+    FT_PNG,
+    FT_SVG,
     n_FT
   };
 
-#define N_EXTENSIONS (n_FT - 1)
-
-struct file_types ft[n_FT] = {
+static const struct file_types ft[n_FT] = {
   {N_("Infer file type from extension"),  NULL},
+  {N_("SPSS Viewer (*.spv)"),             ".spv"},
   {N_("PDF (*.pdf)"),                     ".pdf"},
   {N_("HTML (*.html)"),                   ".html"},
   {N_("OpenDocument (*.odt)"),            ".odt"},
   {N_("Text (*.txt)"),                    ".txt"},
   {N_("Text [plain] (*.txt)"),            ".txt"},
   {N_("PostScript (*.ps)"),               ".ps"},
-  {N_("Comma-Separated Values (*.csv)"),  ".csv"}
+  {N_("Comma-Separated Values (*.csv)"),  ".csv"},
+  {N_("Portable Network Graphics (*.png)"),  ".png"},
+  {N_("Scalable Vector Graphics (*.svg)"),   ".svg"}
 };
 
 
@@ -259,31 +255,25 @@ on_combo_change (GtkFileChooser *chooser)
   gboolean sensitive = FALSE;
   GtkWidget *combo = gtk_file_chooser_get_extra_widget (chooser);
 
-  int x = 0; 
+  int file_type = FT_AUTO;
   gchar *fn = gtk_file_chooser_get_filename (chooser);
 
   if (combo &&  gtk_widget_get_realized (combo))
-    x = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+    file_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
 
-  if (fn == NULL)
-    {
-      sensitive = FALSE;
-    }
-  else
+  if (fn != NULL && file_type == FT_AUTO)
     {
-      gint i;
-      if ( x != 0 )
-       sensitive = TRUE;
-
-      for (i = 1 ; i < N_EXTENSIONS ; ++i)
+      for (gint i = 1 ; i < n_FT ; ++i)
        {
-         if ( g_str_has_suffix (fn, ft[i].ext))
+         if (g_str_has_suffix (fn, ft[i].ext))
            {
              sensitive = TRUE;
              break;
            }
        }
     }
+  else
+    sensitive = (fn != NULL);
 
   g_free (fn);
 
@@ -291,44 +281,13 @@ on_combo_change (GtkFileChooser *chooser)
 }
 
 
-static void
-on_file_chooser_change (GObject *w, GParamSpec *pspec, gpointer data)
-{
-
-  GtkFileChooser *chooser = data;
-  const gchar *name = g_param_spec_get_name (pspec);
-
-  if ( ! gtk_widget_get_realized (GTK_WIDGET (chooser)))
-    return;
-
-  /* Ignore this one.  It causes recursion. */
-  if ( 0 == strcmp ("tooltip-text", name))
-    return;
-
-  on_combo_change (chooser);
-}
-
-
-/* Recursively descend all the children of W, connecting
-   to their "notify" signal */
-static void
-iterate_widgets (GtkWidget *w, gpointer data)
-{
-  if ( GTK_IS_CONTAINER (w))
-    gtk_container_forall (GTK_CONTAINER (w), iterate_widgets, data);
-  else
-    g_signal_connect (w, "notify",  G_CALLBACK (on_file_chooser_change), data);
-}
-
-
-
 static GtkListStore *
 create_file_type_list (void)
 {
   int i;
   GtkTreeIter iter;
   GtkListStore *list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
-  
+
   for (i = 0 ; i < n_FT ; ++i)
     {
       gtk_list_store_append (list, &iter);
@@ -337,7 +296,7 @@ create_file_type_list (void)
                          1,  ft[i].ext,
                          -1);
     }
-  
+
   return list;
 }
 
@@ -349,7 +308,7 @@ psppire_output_window_export (PsppireOutputWindow *window)
   GtkListStore *list;
 
   GtkFileChooser *chooser;
-  
+
   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Export Output"),
                                         GTK_WINDOW (window),
                                         GTK_FILE_CHOOSER_ACTION_SAVE,
@@ -369,7 +328,7 @@ psppire_output_window_export (PsppireOutputWindow *window)
   {
     /* Create text cell renderer */
     GtkCellRenderer *cell = gtk_cell_renderer_text_new();
-    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE );
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE);
 
     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell,  "text", 0);
   }
@@ -380,16 +339,12 @@ psppire_output_window_export (PsppireOutputWindow *window)
 
   gtk_file_chooser_set_extra_widget (chooser, combo);
 
-  /* This kludge is necessary because there is no signal to tell us
-     when the candidate filename of a GtkFileChooser has changed */
-  gtk_container_forall (GTK_CONTAINER (dialog), iterate_widgets, dialog);
-
-
+  g_signal_connect (chooser, "selection-changed", G_CALLBACK (on_combo_change), NULL);
   gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
 
   response = gtk_dialog_run (GTK_DIALOG (dialog));
 
-  if ( response == GTK_RESPONSE_ACCEPT )
+  if (response == GTK_RESPONSE_ACCEPT)
     {
       gint file_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
       gchar *filename = gtk_file_chooser_get_filename (chooser);
@@ -403,9 +358,9 @@ psppire_output_window_export (PsppireOutputWindow *window)
              search for the respective type in the list.
              (It's a O(n) search, but fortunately n is small). */
          gint i;
-         for (i = 1 ; i < N_EXTENSIONS ; ++i)
+         for (i = 1 ; i < n_FT ; ++i)
            {
-             if ( g_str_has_suffix (filename, ft[i].ext))
+             if (g_str_has_suffix (filename, ft[i].ext))
                {
                  file_type = i;
                  break;
@@ -423,12 +378,15 @@ psppire_output_window_export (PsppireOutputWindow *window)
           filename = g_strconcat (filename, ft[file_type].ext, NULL);
           g_free (of);
         }
-      
+
       string_map_init (&options);
       string_map_insert (&options, "output-file", filename);
 
       switch (file_type)
        {
+        case FT_SPV:
+          export_output (window, &options, "spv");
+          break;
        case FT_PDF:
           export_output (window, &options, "pdf");
          break;
@@ -444,19 +402,19 @@ psppire_output_window_export (PsppireOutputWindow *window)
        case FT_CSV:
           export_output (window, &options, "csv");
          break;
+       case FT_PNG:
+          export_output (window, &options, "png");
+         break;
+       case FT_SVG:
+          export_output (window, &options, "svg");
+         break;
 
        case FT_TXT:
           string_map_insert (&options, "box", "unicode");
          /* Fall through */
 
        case FT_ASCII:
-          string_map_insert (&options, "headers", "false");
-          string_map_insert (&options, "paginate", "false");
-          string_map_insert (&options, "squeeze", "true");
-          string_map_insert (&options, "emphasis", "none");
           string_map_insert (&options, "charts", "none");
-          string_map_insert (&options, "top-margin", "0");
-          string_map_insert (&options, "bottom-margin", "0");
           export_output (window, &options, "txt");
          break;
        default:
@@ -475,51 +433,68 @@ static void
 psppire_output_window_init (PsppireOutputWindow *window)
 {
   GtkBuilder *xml = builder_new ("output-window.ui");
-
-  GtkWidget *box = get_widget_assert (xml, "box1");
+  GtkApplication *app = GTK_APPLICATION (g_application_get_default());
+  GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
   gtk_container_add (GTK_CONTAINER (window), box);
 
+  GtkWidget *paned = get_widget_assert (xml, "paned1");
+
   window->dispose_has_run = FALSE;
 
   window->view = psppire_output_view_new (
     GTK_LAYOUT (get_widget_assert (xml, "output")),
-    GTK_TREE_VIEW (get_widget_assert (xml, "overview")),
-    get_action_assert (xml, "edit_copy"),
-    get_action_assert (xml, "edit_select-all"));
-
-
-  connect_help (xml);
+    GTK_TREE_VIEW (get_widget_assert (xml, "overview")));
 
   g_signal_connect (window,
                    "focus-in-event",
                    G_CALLBACK (cancel_urgency),
                    NULL);
 
-  g_signal_connect (get_action_assert (xml,"windows_minimise-all"),
-                   "activate",
-                   G_CALLBACK (psppire_window_minimise_all),
-                   NULL);
+  GObject *menu = get_object_assert (xml, "output-window-menu", G_TYPE_MENU);
+  GtkWidget *menubar = gtk_menu_bar_new_from_model (G_MENU_MODEL (menu));
+  gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (box), paned, TRUE, TRUE, 0);
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menubar),
+                        create_windows_menu (GTK_WINDOW (window)));
+
+  gtk_menu_shell_append (GTK_MENU_SHELL (menubar),
+                        create_help_menu (GTK_WINDOW (window)));
 
   {
-    GtkWidget *w;
-    GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
+    GSimpleAction *print = g_simple_action_new ("print", NULL);
+    g_signal_connect_swapped (print, "activate", G_CALLBACK (psppire_output_window_print), window);
+    g_action_map_add_action (G_ACTION_MAP (window), G_ACTION (print));
 
-    GtkWidget *menubar = get_widget_assert (xml, "menubar");
 
-    gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_help_menu (GTK_WINDOW (window)));
+    const gchar *accels[2] = { "<Primary>P", NULL};
+    gtk_application_set_accels_for_action (app,
+                                          "win.print",
+                                          accels);
+  }
 
-    w = gtk_ui_manager_get_widget (uim,"/ui/menubar/windows_menuitem/windows_minimise-all");
 
-    PSPPIRE_WINDOW (window)->menu =
-      GTK_MENU_SHELL (gtk_widget_get_parent (w));
+  {
+    GSimpleAction *export = g_simple_action_new ("export", NULL);
+    g_signal_connect_swapped (export, "activate", G_CALLBACK (psppire_output_window_export), window);
+    g_action_map_add_action (G_ACTION_MAP (window), G_ACTION (export));
   }
 
-  g_signal_connect_swapped (get_action_assert (xml, "file_export"), "activate",
-                            G_CALLBACK (psppire_output_window_export), window);
+  {
+    GSimpleAction *select_all = g_simple_action_new ("select-all", NULL);
+    g_action_map_add_action (G_ACTION_MAP (window), G_ACTION (select_all));
+  }
 
+  {
+    GSimpleAction *copy = g_simple_action_new ("copy", NULL);
+    g_action_map_add_action (G_ACTION_MAP (window), G_ACTION (copy));
+
+    const gchar *accels[2] = { "<Primary>C", NULL};
+    gtk_application_set_accels_for_action (app,
+                                          "win.copy",
+                                          accels);
+  }
 
-  g_signal_connect_swapped (get_action_assert (xml, "file_print"), "activate",
-                            G_CALLBACK (psppire_output_window_print), window);
 
   g_object_unref (xml);
 
@@ -532,7 +507,7 @@ GtkWidget*
 psppire_output_window_new (void)
 {
   return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
-                                  /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
+                                  /* TRANSLATORS: This will be part of a filename.  Please avoid whitespace. */
                                   "filename", _("Output"),
                                   "description", _("Output Viewer"),
                                   NULL));