Fix GtkCriticals when --no-splash is given
[pspp] / src / ui / gui / main.c
index 002d576ac5c1d5bbd1d55283e56a81a2260280e1..4716b1fa4da6af353e060fa6b22423dc5ae6a11b 100644 (file)
@@ -1,10 +1,9 @@
-/*
-   PSPPIRE --- A Graphical User Interface for PSPP
-   Copyright (C) 2004, 2005, 2006  Free Software Foundation
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2004, 2005, 2006, 2010, 2011, 2012, 2013, 2014, 2015, 2016  Free Software Foundation
 
-   This program is free software; you can redistribute it and/or modify
+   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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-#include <gtk/gtk.h>
-#include "psppire.h"
-#include "progname.h"
-#include <stdlib.h>
-#include <getopt.h>
-
-#include <libpspp/version.h>
-#include <libpspp/copyleft.h>
-
-static gboolean parse_command_line (int *argc, char ***argv, gchar **filename,
-                                   gboolean *show_splash, GError **err);
+#include <config.h>
 
+#include "ui/gui/psppire.h"
 
+#include <gtk/gtk.h>
+#include <stdlib.h>
 
-static GtkWidget *
-create_splash_window (void)
-{
-  GtkWidget *splash ;
-  GtkWidget *image;
+#include "language/lexer/include-path.h"
+#include "libpspp/argv-parser.h"
+#include "libpspp/array.h"
+#include "libpspp/assertion.h"
+#include "libpspp/cast.h"
+#include "libpspp/copyleft.h"
+#include "libpspp/str.h"
+#include "libpspp/string-array.h"
+#include "libpspp/version.h"
+#include "ui/source-init-opts.h"
+#include "ui/gui/psppire-syntax-window.h"
+#include "ui/gui/psppire-data-window.h"
+#include "ui/gui/psppire-output-window.h"
 
-  gtk_window_set_auto_startup_notification (FALSE);
+#include "gl/configmake.h"
+#include "gl/progname.h"
+#include "gl/relocatable.h"
+#include "gl/version-etc.h"
+#include "gl/xalloc.h"
 
-  splash = gtk_window_new (GTK_WINDOW_POPUP);
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
 
-  gtk_window_set_position (GTK_WINDOW (splash),
-                          GTK_WIN_POS_CENTER_ALWAYS);
 
-  image = gtk_image_new_from_file (PKGDATADIR "/splash.png");
 
-  gtk_container_add (GTK_CONTAINER (splash), image);
+static gboolean
+show_version_and_exit ()
+{
+  version_etc (stdout, "psppire", PACKAGE_NAME, PACKAGE_VERSION,
+               "Ben Pfaff", "John Darrington", "Jason Stover", NULL_SENTINEL);
 
-  gtk_widget_show (image);
+  exit (0);
 
-  return splash;
+  return TRUE;
 }
+\f
 
-static gboolean
-hide_splash_window (gpointer data)
+
+gboolean
+init_prepare (GSource * source, gint * timeout_)
 {
-  GtkWidget *splash = data;
-  gtk_widget_hide (splash);
-  gtk_window_set_auto_startup_notification (TRUE);
-  return FALSE;
+  return TRUE;
 }
 
-
-static gboolean
-quit_one_loop (gpointer data)
+gboolean
+init_check (GSource * source)
 {
-  gtk_main_quit ();
-  return FALSE;
+  return TRUE;
 }
 
-
-static gboolean
-run_inner_loop (gpointer data)
+gboolean
+init_dispatch (GSource * ss, GSourceFunc callback, gpointer user_data)
 {
-  initialize ();
+  struct init_source *is = (struct init_source *) ss;
 
-  g_timeout_add (500, hide_splash_window, data);
+  bool finished = initialize (is);
+  is->state++;
 
-  gtk_main ();
-
-  de_initialize ();
+  if (finished)
+    {
+      g_main_loop_quit (is->loop);
+      return FALSE;
+    }
 
-  return FALSE;
+  return TRUE;
 }
 
+static GSourceFuncs init_funcs =
+  { init_prepare, init_check, init_dispatch, NULL };
+\f
 
 
-int
-main (int argc, char *argv[])
+GtkWidget *wsplash = 0;
+gint64 start_time = 0;
+
+
+static GtkWidget *
+create_splash_window (void)
 {
-  GtkWidget *splash_window;
-  gchar *filename = 0;
-  gboolean show_splash = TRUE;
-  GError *err = 0;
-  gchar *vers;
+  GtkWidget *sp = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+  const gchar *filename = PKGDATADIR "/splash.png";
+  const char *relocated_filename = relocate (filename);
+  GtkWidget *l = gtk_image_new_from_file (relocated_filename);
+  if (filename != relocated_filename)
+    free (CONST_CAST (char *, relocated_filename));
+
+  gtk_container_add (GTK_CONTAINER (sp), l);
+  gtk_window_set_type_hint (GTK_WINDOW (sp),
+                            GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
+  gtk_window_set_position (GTK_WINDOW (sp), GTK_WIN_POS_CENTER);
+  gtk_window_set_skip_pager_hint (GTK_WINDOW (sp), TRUE);
+  gtk_window_set_skip_taskbar_hint (GTK_WINDOW (sp), TRUE);
+  gtk_window_set_focus_on_map (GTK_WINDOW (sp), FALSE);
+  gtk_window_set_accept_focus (GTK_WINDOW (sp), FALSE);
+
+  GdkGeometry hints;
+  hints.max_height = 100;
+  hints.max_width = 200;
+  gtk_window_set_geometry_hints (GTK_WINDOW (sp),
+                                 NULL, &hints, GDK_HINT_MAX_SIZE);
+
+
+  gtk_window_set_gravity (GTK_WINDOW (sp), GDK_GRAVITY_CENTER);
+
+  gtk_window_set_modal (GTK_WINDOW (sp), TRUE);
+  gtk_window_set_decorated (GTK_WINDOW (sp), FALSE);
+  gtk_window_set_keep_above (GTK_WINDOW (sp), TRUE);
+  gtk_widget_show_all (sp);
+  return sp;
+}
+
 
-  set_program_name (argv[0]);
+static gint
+on_local_options (GApplication * application,
+                  GVariantDict * options, gpointer user_data)
+{
+  GVariant *b;
 
-  if ( ! gtk_parse_args (&argc, &argv) )
+  b =
+    g_variant_dict_lookup_value (options, "no-splash",
+                                 G_VARIANT_TYPE_BOOLEAN);
+  if (b)
     {
-      perror ("Error parsing arguments");
-      exit (1);
+      g_variant_unref (b);
     }
-
-  if ( (vers = gtk_check_version (GTK_MAJOR_VERSION,
-                                GTK_MINOR_VERSION,
-                                GTK_MICRO_VERSION)) )
+  else
     {
-      g_critical (vers);
+      start_time = g_get_monotonic_time ();
     }
 
-  /* Deal with options like --version, --help etc */
-  if ( ! parse_command_line (&argc, &argv, &filename, &show_splash, &err) )
+  return -1;
+}
+
+
+static void
+on_startup (GApplication * app, gpointer ud)
+{
+  GMainContext *context = g_main_context_new ();
+
+  if (start_time != 0)
     {
-      g_clear_error (&err);
-      return 0;
+      wsplash = create_splash_window ();
+      gtk_application_add_window (GTK_APPLICATION (app),
+                                  GTK_WINDOW (wsplash));
     }
 
-  gdk_init (&argc, &argv);
+  GMainLoop *loop = g_main_loop_new (context, FALSE);
+
+  GSource *ss = g_source_new (&init_funcs, sizeof (struct init_source));
 
-  splash_window = create_splash_window ();
-  if ( show_splash )
-    gtk_widget_show (splash_window);
+  ((struct init_source *) ss)->loop = loop;
+  ((struct init_source *) ss)->state = 0;
 
-  gtk_idle_add (quit_one_loop, 0);
+  g_source_set_priority (ss, G_PRIORITY_DEFAULT);
+
+  g_source_attach (ss, context);
+  g_main_loop_run (loop);
+}
 
-  gtk_quit_add (0, run_inner_loop, splash_window);
-  gtk_main ();
 
+static void
+post_initialise (GApplication * app)
+{
+  register_selection_functions ();
+  psppire_output_window_setup ();
 
-  return 0;
+  GSimpleAction *quit = g_simple_action_new ("quit", NULL);
+  g_signal_connect_swapped (quit, "activate", G_CALLBACK (psppire_quit), app);
+  g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (quit));
 }
 
 
-/* Parses the command line specified by ARGC and ARGV as received by
-   main ().  Returns true if normal execution should proceed,
-   false if the command-line indicates that PSPP should exit. */
+#define SPLASH_DURATION 1000
+
 static gboolean
-parse_command_line (int *argc, char ***argv, gchar **filename,
-                   gboolean *show_splash, GError **err)
+destroy_splash (gpointer ud)
 {
+  GtkWidget *sp = GTK_WIDGET (ud);
+  gtk_widget_destroy (sp);
+  wsplash = NULL;
+  return G_SOURCE_REMOVE;
+}
 
-  static struct option long_options[] =
-    {
-      {"help", no_argument, NULL, 'h'},
-      {"version", no_argument, NULL, 'V'},
-      {"no-splash", no_argument, NULL, 'q'},
-      {0, 0, 0, 0},
-    };
-
-  int c;
+static void
+on_activate (GApplication * app, gpointer ud)
+{
+  post_initialise (app);
 
-  for (;;)
+  GtkWindow *x = create_data_window ();
+  if (wsplash)
     {
-      c = getopt_long (*argc, *argv, "hVq", long_options, NULL);
-      if (c == -1)
-       break;
-
-      switch (c)
-       {
-       case 'h':
-         g_print ("Usage: psppire {|--help|--version|--no-splash}\n");
-          return FALSE;
-       case 'V':
-         g_print (version);
-         g_print ("\n");
-         g_print (legal);
-         return FALSE;
-       case 'q':
-         *show_splash = FALSE;
-         break;
-       default:
-         return FALSE;
-       }
+      gtk_window_set_transient_for (GTK_WINDOW (wsplash), GTK_WINDOW (x));
+      gtk_application_add_window (GTK_APPLICATION (app), x);
+      gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (wsplash));
+      gtk_window_set_keep_above (GTK_WINDOW (wsplash), TRUE);
+      gtk_window_present (GTK_WINDOW (wsplash));
+
+      /* Remove the splash screen after SPLASH_DURATION milliseconds */
+      gint64 elapsed_time = (g_get_monotonic_time () - start_time) / 1000;
+      if (SPLASH_DURATION - elapsed_time <= 0)
+       destroy_splash (wsplash);
+      else
+       g_timeout_add (SPLASH_DURATION - elapsed_time, destroy_splash, wsplash);
     }
+}
 
-  if ( optind < *argc)
-    {
-      *filename = (*argv)[optind];
-    }
 
-  return TRUE;
+static void
+on_open (GApplication * app, GFile ** files, gint n_files, gchar * hint,
+         gpointer ud)
+{
+  post_initialise (app);
+
+  gchar *file = g_file_get_parse_name (files[0]);
+  psppire_preload_file (file);
+  g_free (file);
+}
+
+
+/* These are arguments which must be processed BEFORE the X server has been initialised */
+static void
+process_pre_start_arguments (int *argc, char ***argv)
+{
+  GOptionEntry oe[] = {
+    {"version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+     show_version_and_exit, "Show version information and exit", 0},
+    {NULL}
+  };
+
+  GOptionContext *oc = g_option_context_new ("");
+  g_option_context_set_help_enabled (oc, FALSE);
+  g_option_context_set_ignore_unknown_options (oc, FALSE);
+  g_option_context_add_main_entries (oc, oe, NULL);
+  g_option_context_parse (oc, argc, argv, NULL);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  GtkApplication *app =
+    gtk_application_new ("gnu.pspp", G_APPLICATION_HANDLES_OPEN);
+
+  process_pre_start_arguments (&argc, &argv);
+
+  GOptionEntry oe[] = {
+    {"no-splash", 'q', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL,
+     "Do not display the splash screen", 0},
+    {NULL}
+  };
+
+  g_application_add_main_option_entries (G_APPLICATION (app), oe);
+
+  g_signal_connect (app, "startup", G_CALLBACK (on_startup), NULL);
+  g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL);
+  g_signal_connect (app, "handle-local-options",
+                    G_CALLBACK (on_local_options), NULL);
+  g_signal_connect (app, "open", G_CALLBACK (on_open), NULL);
+
+  {
+    GSimpleAction *act_new_syntax = g_simple_action_new ("new-syntax", NULL);
+    g_signal_connect_swapped (act_new_syntax, "activate",
+                              G_CALLBACK (create_syntax_window), NULL);
+    g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_new_syntax));
+  }
+
+  {
+    GSimpleAction *act_new_data = g_simple_action_new ("new-data", NULL);
+    g_signal_connect_swapped (act_new_data, "activate",
+                              G_CALLBACK (create_data_window), NULL);
+    g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_new_data));
+  }
+
+  return g_application_run (G_APPLICATION (app), argc, argv);
 }