Fix problem with non-reentrant journal driver
[pspp] / src / ui / gui / psppire.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2004, 2005, 2006, 2009, 2010, 2011, 2012, 2013  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
20 #include <assert.h>
21 #include <gsl/gsl_errno.h>
22 #include <gtk/gtk.h>
23 #include <libintl.h>
24 #include <unistd.h>
25
26 #include "data/any-reader.h"
27 #include "data/casereader.h"
28 #include "data/dataset.h"
29 #include "data/datasheet.h"
30 #include "data/file-handle-def.h"
31 #include "data/file-name.h"
32 #include "data/por-file-reader.h"
33 #include "data/session.h"
34 #include "data/settings.h"
35 #include "data/sys-file-reader.h"
36
37 #include "language/lexer/lexer.h"
38 #include "libpspp/i18n.h"
39 #include "libpspp/message.h"
40 #include "libpspp/version.h"
41
42 #include "output/driver.h"
43 #include "output/journal.h"
44 #include "output/message-item.h"
45
46 #include "ui/gui/dict-display.h"
47 #include "ui/gui/executor.h"
48 #include "ui/gui/psppire-data-store.h"
49 #include "ui/gui/psppire-data-window.h"
50 #include "ui/gui/psppire-dict.h"
51 #include "ui/gui/psppire.h"
52 #include "ui/gui/psppire-output-window.h"
53 #include "ui/gui/psppire-syntax-window.h"
54 #include "ui/gui/psppire-selector.h"
55 #include "ui/gui/psppire-var-view.h"
56 #include "ui/gui/psppire-window-register.h"
57 #include "ui/gui/widgets.h"
58 #include "ui/source-init-opts.h"
59 #include "ui/syntax-gen.h"
60
61 #include "ui/gui/icons/icon-names.h"
62
63
64 #include "gl/configmake.h"
65 #include "gl/xalloc.h"
66 #include "gl/relocatable.h"
67
68 static void inject_renamed_icons (void);
69 static void create_icon_factory (void);
70 static gchar *local_to_filename_encoding (const char *fn);
71
72
73 #define _(msgid) gettext (msgid)
74 #define N_(msgid) msgid
75
76
77 void
78 initialize (const char *data_file)
79 {
80   i18n_init ();
81
82   preregister_widgets ();
83
84   gsl_set_error_handler_off ();
85   settings_init ();
86   fh_init ();
87
88   psppire_set_lexer (NULL);
89
90   bind_textdomain_codeset (PACKAGE, "UTF-8");
91
92   inject_renamed_icons ();
93   create_icon_factory ();
94
95   psppire_output_window_setup ();
96
97   journal_init ();
98   textdomain (PACKAGE);
99
100   psppire_selector_set_default_selection_func (GTK_TYPE_ENTRY, insert_source_row_into_entry);
101   psppire_selector_set_default_selection_func (PSPPIRE_VAR_VIEW_TYPE, insert_source_row_into_tree_view);
102   psppire_selector_set_default_selection_func (GTK_TYPE_TREE_VIEW, insert_source_row_into_tree_view);
103
104   if (data_file)
105     {
106       gchar *filename = local_to_filename_encoding (data_file);
107
108       /* Check to see if the file is a .sav or a .por file.  If not
109          assume that it is a syntax file */
110       if ( any_reader_may_open (filename))
111         open_data_window (NULL, filename, NULL);
112       else
113         {
114           create_data_window ();
115           open_syntax_window (filename, NULL);
116         }
117
118       g_free (filename);
119     }
120   else
121     create_data_window ();
122 }
123
124
125 void
126 de_initialize (void)
127 {
128   settings_done ();
129   output_close ();
130   i18n_done ();
131 }
132
133 static void
134 func (gpointer key, gpointer value, gpointer data)
135 {
136   gboolean rv;
137   PsppireWindow *window = PSPPIRE_WINDOW (value);
138
139   g_signal_emit_by_name (window, "delete-event", 0, &rv);
140 }
141
142 void
143 psppire_quit (void)
144 {
145   PsppireWindowRegister *reg = psppire_window_register_new ();
146   psppire_window_register_foreach (reg, func, NULL);
147
148   gtk_main_quit ();
149 }
150
151 static void
152 inject_renamed_icon (const char *icon, const char *substitute)
153 {
154   GtkIconTheme *theme = gtk_icon_theme_get_default ();
155   if (!gtk_icon_theme_has_icon (theme, icon)
156       && gtk_icon_theme_has_icon (theme, substitute))
157     {
158       gint *sizes = gtk_icon_theme_get_icon_sizes (theme, substitute);
159       gint *p;
160
161       for (p = sizes; *p != 0; p++)
162         {
163           gint size = *p;
164           GdkPixbuf *pb;
165
166           pb = gtk_icon_theme_load_icon (theme, substitute, size, 0, NULL);
167           if (pb != NULL)
168             {
169               GdkPixbuf *copy = gdk_pixbuf_copy (pb);
170               if (copy != NULL)
171                 gtk_icon_theme_add_builtin_icon (icon, size, copy);
172             }
173         }
174     }
175 }
176
177 /* Avoid a bug in GTK+ 2.22 that can cause a segfault at startup time.  Earlier
178    and later versions of GTK+ do not have the bug.  Bug #31511.
179
180    Based on this patch against Inkscape:
181    https://launchpadlibrarian.net/60175914/copy_renamed_icons.patch */
182 static void
183 inject_renamed_icons (void)
184 {
185   if (gtk_major_version == 2 && gtk_minor_version == 22)
186     {
187       inject_renamed_icon ("gtk-file", "document-x-generic");
188       inject_renamed_icon ("gtk-directory", "folder");
189     }
190 }
191
192
193 struct icon_size
194 {
195   int resolution;  /* The dimension of the images which will be used */
196   size_t n_sizes;  /* The number of items in the array below.
197                       This may be zero, in which case the iconset will be wildcarded
198                       (used by default when no non-wildcarded set is available) */
199   const GtkIconSize *usage; /* An array determining for what the icon set is used */
200 };
201
202 static const GtkIconSize menus[] = {GTK_ICON_SIZE_MENU};
203 static const GtkIconSize toolbar[] = {GTK_ICON_SIZE_LARGE_TOOLBAR};
204
205
206 /* We currently have three icon sets viz: 16x16, 24x24 and 32x32
207    We use the 16x16 for menus, the 32x32 for the toolbar and 
208    the 24x24 for everything else.
209
210    Exactly one element of the following array should have its 2nd and 3rd
211    argument as zero.
212 */
213 static const struct icon_size sizemap[] = 
214 {
215   {16,  sizeof (menus) / sizeof (GtkIconSize), menus},
216   {24, 0, 0},
217   {32,  sizeof (toolbar) / sizeof (GtkIconSize), toolbar}
218 };
219
220
221 static void
222 create_icon_factory (void)
223 {
224   gint c;
225   GtkIconFactory *factory = gtk_icon_factory_new ();
226   struct icon_context ctx[2];
227   ctx[0] = action_icon_context;
228   ctx[1] = category_icon_context;
229   for (c = 0 ; c < 2 ; ++c)
230   {
231     const struct icon_context *ic = &ctx[c];
232     gint i;
233     for (i = 0 ; i < ic->n_icons ; ++i)
234       {
235         GtkIconSet *icon_set = gtk_icon_set_new ();
236         int r;
237         for (r = 0 ; r < sizeof (sizemap) / sizeof (sizemap[0]); ++r)
238           {
239             int s;
240             GtkIconSource *source = gtk_icon_source_new ();
241             gchar *filename = g_strdup_printf ("%s/%s/%dx%d/%s.png", PKGDATADIR,
242                                                ic->context_name,
243                                                sizemap[r].resolution, sizemap[r].resolution,
244                                                ic->icon_name[i]);
245             const char *relocated_filename = relocate (filename);
246
247             gtk_icon_source_set_filename (source, relocated_filename);
248             gtk_icon_source_set_size_wildcarded (source, sizemap[r].n_sizes <= 0);
249             for (s = 0 ; s < sizemap[r].n_sizes ; ++s)
250               gtk_icon_source_set_size (source, sizemap[r].usage[s]);
251             if (filename != relocated_filename)
252               free (CONST_CAST (char *, relocated_filename));
253             g_free (filename);
254             gtk_icon_set_add_source (icon_set, source);
255           }
256       
257         gtk_icon_factory_add (factory, ic->icon_name[i], icon_set);
258     }
259   }
260
261   {
262     struct iconmap
263     {
264       const gchar *gtk_id;
265       gchar *pspp_id;
266     };
267
268     /* We have our own icons for some things.
269        But we want the Stock Item to be identical to the Gtk standard
270        ones in all other respects.
271     */
272     const struct iconmap map[] = {
273       {GTK_STOCK_NEW,   "file-new-document"},
274       {GTK_STOCK_QUIT,  "file-quit"},
275       {GTK_STOCK_SAVE,  "file-save-document"},
276       {GTK_STOCK_CUT,   "edit-cut"},
277       {GTK_STOCK_COPY,  "edit-copy"},
278       {GTK_STOCK_PASTE, "edit-paste"},
279       {GTK_STOCK_ABOUT, "help-about"},
280       {GTK_STOCK_PRINT, "file-print-document"}
281     };
282
283     GtkStockItem customised[sizeof (map) / sizeof (map[0])];
284     int i;
285
286     for (i = 0; i < sizeof (map) / sizeof (map[0]); ++i)
287     {
288       gtk_stock_lookup (map[i].gtk_id, &customised[i]);
289       customised[i].stock_id =  map[i].pspp_id;
290     }
291
292
293
294     gtk_stock_add (customised, sizeof (map) / sizeof (map[0]));
295   }
296
297   {
298     /* Create our own "pspp-stock-reset" item, using the
299        GTK_STOCK_REFRESH icon set */
300     GtkStockItem items[2] = {
301       {"pspp-stock-reset", N_("_Reset"), 0, 0, PACKAGE},
302       {"pspp-stock-select", N_("_Select"), 0, 0, PACKAGE}
303     };
304
305     gtk_stock_add (items, 2);
306
307     gtk_icon_factory_add (factory, "pspp-stock-reset",
308                           gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
309                           );
310
311     gtk_icon_factory_add (factory, "pspp-stock-select",
312                           gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
313                           );
314   }
315
316   gtk_icon_factory_add_default (factory);
317 }
318 \f
319 /* 
320    Convert a filename from the local encoding into "filename" encoding.
321    The return value will be allocated on the heap.  It is the responsibility
322    of the caller to free it.
323  */
324 static gchar *
325 local_to_filename_encoding (const char *fn)
326 {
327   gchar *filename = NULL;
328   gchar *utf8 = NULL;
329   const gchar *local_encoding = NULL;
330   gsize written = -1;
331   const gboolean local_is_utf8 = g_get_charset (&local_encoding);
332
333   /* There seems to be no Glib function to convert from local encoding
334      to filename encoding.  Therefore it has to be done in two steps:
335      the intermediate encoding is UTF8.
336
337      Either step could fail.  However, in many cases the file can still
338      be loaded even if the conversion fails. So in those cases, after showing
339      a warning, we simply copy the locally encoded filename to the destination
340      and hope for the best.
341   */
342
343   if ( local_is_utf8)
344     {
345       utf8 = xstrdup (fn);
346     }
347   else
348     {
349       GError *err = NULL;
350       utf8 = g_locale_to_utf8 (fn, -1, NULL, &written, &err);
351       if ( NULL == utf8)
352         {
353           g_warning ("Cannot convert filename from local encoding `%s' to UTF-8: %s",
354                      local_encoding,
355                      err->message);
356           g_clear_error (&err);
357         }
358     }
359
360   if ( NULL != utf8)
361     {
362       GError *err = NULL;
363       filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
364       if ( NULL == filename)
365         {
366           g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
367                      err->message);
368           g_clear_error (&err);
369         }
370     }
371
372   g_free (utf8);
373
374   if ( filename == NULL)
375     filename = xstrdup (fn);
376
377   return filename;
378 }
379
380 static void
381 handle_msg (const struct msg *m_, void *lexer_)
382 {
383   struct lexer *lexer = lexer_;
384   struct msg m = *m_;
385
386   if (lexer != NULL && m.file_name == NULL)
387     {
388       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
389       m.first_line = lex_get_first_line_number (lexer, 0);
390       m.last_line = lex_get_last_line_number (lexer, 0);
391       m.first_column = lex_get_first_column (lexer, 0);
392       m.last_column = lex_get_last_column (lexer, 0);
393     }
394
395   message_item_submit (message_item_create (&m));
396 }
397
398 void
399 psppire_set_lexer (struct lexer *lexer)
400 {
401   msg_set_handler (handle_msg, lexer);
402 }