Whitespace changes only: Remove trailing whitespace
[pspp] / src / ui / gui / main.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2004, 2005, 2006, 2010, 2011, 2012, 2013, 2014, 2015, 2016  Free Software Foundation
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "ui/gui/psppire.h"
20
21 #include <gtk/gtk.h>
22 #include <stdlib.h>
23
24 #include "language/lexer/include-path.h"
25 #include "libpspp/argv-parser.h"
26 #include "libpspp/array.h"
27 #include "libpspp/assertion.h"
28 #include "libpspp/cast.h"
29 #include "libpspp/copyleft.h"
30 #include "libpspp/str.h"
31 #include "libpspp/string-array.h"
32 #include "libpspp/version.h"
33 #include "ui/source-init-opts.h"
34 #include "ui/gui/psppire-syntax-window.h"
35 #include "ui/gui/psppire-data-window.h"
36 #include "ui/gui/psppire-output-window.h"
37
38 #include "gl/configmake.h"
39 #include "gl/progname.h"
40 #include "gl/relocatable.h"
41 #include "gl/version-etc.h"
42 #include "gl/xalloc.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46 #define N_(msgid) msgid
47
48
49
50 static gboolean
51 show_version_and_exit ()
52 {
53   version_etc (stdout, "psppire", PACKAGE_NAME, PACKAGE_VERSION,
54                "Ben Pfaff", "John Darrington", "Jason Stover", NULL_SENTINEL);
55
56   exit (0);
57
58   return TRUE;
59 }
60
61 \f
62
63 gboolean
64 init_prepare (GSource * source, gint * timeout_)
65 {
66   return TRUE;
67 }
68
69 gboolean
70 init_check (GSource * source)
71 {
72   return TRUE;
73 }
74
75 gboolean
76 init_dispatch (GSource * ss, GSourceFunc callback, gpointer user_data)
77 {
78   struct init_source *is = (struct init_source *) ss;
79
80   bool finished = initialize (is);
81   is->state++;
82
83   if (finished)
84     {
85       g_main_loop_quit (is->loop);
86       return FALSE;
87     }
88
89   return TRUE;
90 }
91
92 static GSourceFuncs init_funcs =
93   { init_prepare, init_check, init_dispatch, NULL };
94 \f
95
96
97 GtkWidget *wsplash = 0;
98 gint64 start_time = 0;
99
100
101 static GtkWidget *
102 create_splash_window (void)
103 {
104   GtkWidget *sp = gtk_window_new (GTK_WINDOW_TOPLEVEL);
105
106   const gchar *filename = PKGDATADIR "/splash.png";
107   const char *relocated_filename = relocate (filename);
108   GtkWidget *l = gtk_image_new_from_file (relocated_filename);
109   if (filename != relocated_filename)
110     free (CONST_CAST (char *, relocated_filename));
111
112   gtk_container_add (GTK_CONTAINER (sp), l);
113   gtk_window_set_type_hint (GTK_WINDOW (sp),
114                             GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
115   gtk_window_set_position (GTK_WINDOW (sp), GTK_WIN_POS_CENTER);
116   gtk_window_set_skip_pager_hint (GTK_WINDOW (sp), TRUE);
117   gtk_window_set_skip_taskbar_hint (GTK_WINDOW (sp), TRUE);
118   gtk_window_set_focus_on_map (GTK_WINDOW (sp), FALSE);
119   gtk_window_set_accept_focus (GTK_WINDOW (sp), FALSE);
120
121   GdkGeometry hints;
122   hints.max_height = 100;
123   hints.max_width = 200;
124   gtk_window_set_geometry_hints (GTK_WINDOW (sp),
125                                  NULL, &hints, GDK_HINT_MAX_SIZE);
126
127
128   gtk_window_set_gravity (GTK_WINDOW (sp), GDK_GRAVITY_CENTER);
129
130   gtk_window_set_modal (GTK_WINDOW (sp), TRUE);
131   gtk_window_set_decorated (GTK_WINDOW (sp), FALSE);
132   gtk_window_set_keep_above (GTK_WINDOW (sp), TRUE);
133   gtk_widget_show_all (sp);
134   return sp;
135 }
136
137
138 static gint
139 on_local_options (GApplication * application,
140                   GVariantDict * options, gpointer user_data)
141 {
142   {
143     GVariant *b =
144       g_variant_dict_lookup_value (options, "no-unique",
145                                    G_VARIANT_TYPE_BOOLEAN);
146     if (b)
147       {
148         GApplicationFlags flags =  g_application_get_flags (application);
149         flags |= G_APPLICATION_NON_UNIQUE;
150         g_application_set_flags (application, flags);
151         g_variant_unref (b);
152       }
153   }
154   {
155     GVariant *b =
156       g_variant_dict_lookup_value (options, "no-splash",
157                                    G_VARIANT_TYPE_BOOLEAN);
158     if (b)
159       g_variant_unref (b);
160     else
161       start_time = g_get_monotonic_time ();
162   }
163
164
165   return -1;
166 }
167
168
169 static void
170 on_startup (GApplication * app, gpointer ud)
171 {
172   GMainContext *context = g_main_context_new ();
173
174   if (start_time != 0)
175     {
176       wsplash = create_splash_window ();
177       gtk_application_add_window (GTK_APPLICATION (app),
178                                   GTK_WINDOW (wsplash));
179     }
180
181   GMainLoop *loop = g_main_loop_new (context, FALSE);
182
183   GSource *ss = g_source_new (&init_funcs, sizeof (struct init_source));
184
185   ((struct init_source *) ss)->loop = loop;
186   ((struct init_source *) ss)->state = 0;
187
188   g_source_set_priority (ss, G_PRIORITY_DEFAULT);
189
190   g_source_attach (ss, context);
191   g_main_loop_run (loop);
192 }
193
194
195 static void
196 post_initialise (GApplication * app)
197 {
198   register_selection_functions ();
199   psppire_output_window_setup ();
200
201   GSimpleAction *quit = g_simple_action_new ("quit", NULL);
202   g_signal_connect_swapped (quit, "activate", G_CALLBACK (psppire_quit), app);
203   g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (quit));
204 }
205
206
207 #define SPLASH_DURATION 1000
208
209 static gboolean
210 destroy_splash (gpointer ud)
211 {
212   GtkWidget *sp = GTK_WIDGET (ud);
213   gtk_widget_destroy (sp);
214   wsplash = NULL;
215   return G_SOURCE_REMOVE;
216 }
217
218
219 static void
220 wait_for_splash (GApplication *app, GtkWindow *x)
221 {
222   if (wsplash)
223     {
224       gtk_window_set_transient_for (GTK_WINDOW (wsplash), x);
225       gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (wsplash));
226       gtk_window_set_keep_above (GTK_WINDOW (wsplash), TRUE);
227       gtk_window_present (GTK_WINDOW (wsplash));
228
229       /* Remove the splash screen after SPLASH_DURATION milliseconds */
230       gint64 elapsed_time = (g_get_monotonic_time () - start_time) / 1000;
231       if (SPLASH_DURATION - elapsed_time <= 0)
232         destroy_splash (wsplash);
233       else
234         g_timeout_add (SPLASH_DURATION - elapsed_time, destroy_splash, wsplash);
235     }
236 }
237
238
239 static void
240 on_activate (GApplication * app, gpointer ud)
241 {
242   post_initialise (app);
243
244   GtkWindow *x = create_data_window ();
245   gtk_application_add_window (GTK_APPLICATION (app), x);
246
247   wait_for_splash (app, x);
248 }
249
250
251 static void
252 on_open (GApplication *app, GFile **files, gint n_files, gchar * hint,
253          gpointer ud)
254 {
255   post_initialise (app);
256
257   gchar *file = g_file_get_parse_name (files[0]);
258   GtkWindow *x = psppire_preload_file (file);
259   g_free (file);
260
261   wait_for_splash (app, x);
262 }
263
264
265 /* These are arguments which must be processed BEFORE the X server has been initialised */
266 static void
267 process_pre_start_arguments (int *argc, char ***argv)
268 {
269   GOptionEntry oe[] = {
270     {"version", 'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
271      show_version_and_exit, N_("Show version information and exit"), 0},
272     {NULL}
273   };
274
275   GOptionContext *oc = g_option_context_new ("");
276   g_option_context_set_help_enabled (oc, FALSE);
277   g_option_context_set_ignore_unknown_options (oc, FALSE);
278   g_option_context_add_main_entries (oc, oe, NULL);
279   g_option_context_parse (oc, argc, argv, NULL);
280 }
281
282
283 int
284 main (int argc, char *argv[])
285 {
286   set_program_name (argv[0]);
287
288   GtkApplication *app =
289     gtk_application_new ("gnu.pspp", G_APPLICATION_HANDLES_OPEN);
290
291   process_pre_start_arguments (&argc, &argv);
292
293   GOptionEntry oe[] = {
294     {"no-splash", 'q', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL,
295       N_("Do not display the splash screen"), 0},
296     {"no-unique", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL,
297       N_("Do not attempt single instance negotiation"), 0},
298     {NULL}
299   };
300
301   g_application_add_main_option_entries (G_APPLICATION (app), oe);
302
303   g_signal_connect (app, "startup", G_CALLBACK (on_startup), NULL);
304   g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL);
305   g_signal_connect (app, "handle-local-options",
306                     G_CALLBACK (on_local_options), NULL);
307   g_signal_connect (app, "open", G_CALLBACK (on_open), NULL);
308
309   {
310     GSimpleAction *act_new_syntax = g_simple_action_new ("new-syntax", NULL);
311     g_signal_connect_swapped (act_new_syntax, "activate",
312                               G_CALLBACK (create_syntax_window), NULL);
313     g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_new_syntax));
314   }
315
316   {
317     GSimpleAction *act_new_data = g_simple_action_new ("new-data", NULL);
318     g_signal_connect_swapped (act_new_data, "activate",
319                               G_CALLBACK (create_data_window), NULL);
320     g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_new_data));
321   }
322
323   return g_application_run (G_APPLICATION (app), argc, argv);
324 }