gui: Add workaround for GTK+ 2.20 crash at startup.
[pspp] / src / ui / gui / psppire.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2004, 2005, 2006, 2009, 2010, 2011  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 <assert.h>
20 #include <gsl/gsl_errno.h>
21 #include <gtk/gtk.h>
22 #include <libintl.h>
23 #include <unistd.h>
24
25 #include "data/casereader.h"
26 #include "data/dataset.h"
27 #include "data/datasheet.h"
28 #include "data/file-handle-def.h"
29 #include "data/file-name.h"
30 #include "data/por-file-reader.h"
31 #include "data/session.h"
32 #include "data/settings.h"
33 #include "data/sys-file-reader.h"
34
35 #include "language/lexer/lexer.h"
36 #include "libpspp/i18n.h"
37 #include "libpspp/message.h"
38 #include "libpspp/version.h"
39
40 #include "output/driver.h"
41 #include "output/journal.h"
42 #include "output/message-item.h"
43
44 #include "ui/gui/dict-display.h"
45 #include "ui/gui/executor.h"
46 #include "ui/gui/psppire-data-store.h"
47 #include "ui/gui/psppire-data-window.h"
48 #include "ui/gui/psppire-dict.h"
49 #include "ui/gui/psppire.h"
50 #include "ui/gui/psppire-output-window.h"
51 #include "ui/gui/psppire-selector.h"
52 #include "ui/gui/psppire-var-store.h"
53 #include "ui/gui/psppire-var-view.h"
54 #include "ui/gui/psppire-window-register.h"
55 #include "ui/gui/widgets.h"
56 #include "ui/source-init-opts.h"
57 #include "ui/syntax-gen.h"
58
59 #include "gl/configmake.h"
60 #include "gl/xalloc.h"
61 #include "gl/relocatable.h"
62
63 GtkRecentManager *the_recent_mgr;
64
65 static void inject_renamed_icons (void);
66 static void create_icon_factory (void);
67 static void load_data_file (PsppireDataWindow *, const char *);
68
69 #define _(msgid) gettext (msgid)
70 #define N_(msgid) msgid
71
72
73 void
74 initialize (const char *data_file)
75 {
76   PsppireDataWindow *data_window;
77
78   i18n_init ();
79
80   preregister_widgets ();
81
82   gsl_set_error_handler_off ();
83   settings_init ();
84   fh_init ();
85
86   psppire_set_lexer (NULL);
87
88   bind_textdomain_codeset (PACKAGE, "UTF-8");
89
90   inject_renamed_icons ();
91   create_icon_factory ();
92
93   psppire_output_window_setup ();
94
95   journal_enable ();
96   textdomain (PACKAGE);
97
98
99   the_recent_mgr = gtk_recent_manager_get_default ();
100
101   psppire_selector_set_default_selection_func (GTK_TYPE_ENTRY, insert_source_row_into_entry);
102   psppire_selector_set_default_selection_func (PSPPIRE_VAR_VIEW_TYPE, insert_source_row_into_tree_view);
103   psppire_selector_set_default_selection_func (GTK_TYPE_TREE_VIEW, insert_source_row_into_tree_view);
104
105   data_window = psppire_default_data_window ();
106   if (data_file != NULL)
107     load_data_file (data_window, data_file);
108   execute_const_syntax_string (data_window, "");
109 }
110
111
112 void
113 de_initialize (void)
114 {
115   settings_done ();
116   output_close ();
117   i18n_done ();
118 }
119
120 static void
121 func (gpointer key, gpointer value, gpointer data)
122 {
123   gboolean rv;
124   PsppireWindow *window = PSPPIRE_WINDOW (value);
125
126   g_signal_emit_by_name (window, "delete-event", 0, &rv);
127 }
128
129 void
130 psppire_quit (void)
131 {
132   PsppireWindowRegister *reg = psppire_window_register_new ();
133   psppire_window_register_foreach (reg, func, NULL);
134
135   gtk_main_quit ();
136 }
137
138 static void
139 inject_renamed_icon (const char *icon, const char *substitute)
140 {
141   GtkIconTheme *theme = gtk_icon_theme_get_default ();
142   if (!gtk_icon_theme_has_icon (theme, icon)
143       && gtk_icon_theme_has_icon (theme, substitute))
144     {
145       gint *sizes = gtk_icon_theme_get_icon_sizes (theme, substitute);
146       gint *p;
147
148       for (p = sizes; *p != 0; p++)
149         {
150           gint size = *p;
151           GdkPixbuf *pb;
152
153           pb = gtk_icon_theme_load_icon (theme, substitute, size, 0, NULL);
154           if (pb != NULL)
155             {
156               GdkPixbuf *copy = gdk_pixbuf_copy (pb);
157               if (copy != NULL)
158                 gtk_icon_theme_add_builtin_icon (icon, size, copy);
159             }
160         }
161     }
162 }
163
164 /* Avoid a bug in GTK+ 2.22 that can cause a segfault at startup time.  Earlier
165    and later versions of GTK+ do not have the bug.  Bug #31511.
166
167    Based on this patch against Inkscape:
168    https://launchpadlibrarian.net/60175914/copy_renamed_icons.patch */
169 static void
170 inject_renamed_icons (void)
171 {
172   if (gtk_major_version == 2 && gtk_minor_version == 22)
173     {
174       inject_renamed_icon ("gtk-file", "document-x-generic");
175       inject_renamed_icon ("gtk-directory", "folder");
176     }
177 }
178
179 struct icon_info
180 {
181   const char *file_name;
182   const gchar *id;
183 };
184
185
186 static const struct icon_info icons[] =
187   {
188     {PKGDATADIR "/value-labels.png",    "pspp-value-labels"},
189     {PKGDATADIR "/weight-cases.png",    "pspp-weight-cases"},
190     {PKGDATADIR "/goto-variable.png",   "pspp-goto-variable"},
191     {PKGDATADIR "/insert-variable.png", "pspp-insert-variable"},
192     {PKGDATADIR "/insert-case.png",     "pspp-insert-case"},
193     {PKGDATADIR "/split-file.png",      "pspp-split-file"},
194     {PKGDATADIR "/select-cases.png",    "pspp-select-cases"},
195     {PKGDATADIR "/recent-dialogs.png",  "pspp-recent-dialogs"},
196     {PKGDATADIR "/nominal.png",         "var-nominal"},
197     {PKGDATADIR "/ordinal.png",         "var-ordinal"},
198     {PKGDATADIR "/scale.png",           "var-scale"},
199     {PKGDATADIR "/string.png",          "var-string"},
200     {PKGDATADIR "/date-scale.png",      "var-date-scale"}
201   };
202
203 static void
204 create_icon_factory (void)
205 {
206   gint i;
207   GtkIconFactory *factory = gtk_icon_factory_new ();
208
209   for (i = 0 ; i < sizeof (icons) / sizeof(icons[0]); ++i)
210     {
211       GError *err = NULL;
212       GdkPixbuf *pixbuf =
213         gdk_pixbuf_new_from_file (relocate (icons[i].file_name), &err);
214
215       if ( pixbuf )
216         {
217           GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
218           g_object_unref (pixbuf);
219           gtk_icon_factory_add ( factory, icons[i].id, icon_set);
220         }
221       else
222         {
223           g_warning ("Cannot create icon: %s", err->message);
224           g_clear_error (&err);
225         }
226     }
227
228   {
229     /* Create our own "pspp-stock-reset" item, using the
230        GTK_STOCK_REFRESH icon set */
231
232     GtkStockItem items[] = {
233       {"pspp-stock-reset", N_("_Reset"), 0, 0, PACKAGE},
234       {"pspp-stock-select", N_("_Select"), 0, 0, PACKAGE}
235     };
236
237
238     gtk_stock_add (items, 2);
239     gtk_icon_factory_add (factory, "pspp-stock-reset",
240                           gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
241                           );
242
243     gtk_icon_factory_add (factory, "pspp-stock-select",
244                           gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
245                           );
246   }
247
248   gtk_icon_factory_add_default (factory);
249 }
250 \f
251 static void
252 load_data_file (PsppireDataWindow *window, const char *arg)
253 {
254   gchar *filename = NULL;
255   gchar *utf8 = NULL;
256   const gchar *local_encoding = NULL;
257   gsize written = -1;
258   const gboolean local_is_utf8 = g_get_charset (&local_encoding);
259
260   /* There seems to be no Glib function to convert from local encoding
261      to filename encoding.  Therefore it has to be done in two steps:
262      the intermediate encoding is UTF8.
263
264      Either step could fail.  However, in many cases the file can still
265      be loaded even if the conversion fails. So in those cases, after showing
266      a warning, we simply copy the locally encoded filename to the destination
267      and hope for the best.
268   */
269
270   if ( local_is_utf8)
271     {
272       utf8 = xstrdup (arg);
273     }
274   else
275     {
276       GError *err = NULL;
277       utf8 = g_locale_to_utf8 (arg, -1, NULL, &written, &err);
278       if ( NULL == utf8)
279         {
280           g_warning ("Cannot convert filename from local encoding `%s' to UTF-8: %s",
281                      local_encoding,
282                      err->message);
283           g_clear_error (&err);
284         }
285     }
286
287   if ( NULL != utf8)
288     {
289       GError *err = NULL;
290       filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
291       if ( NULL == filename)
292         {
293           g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
294                      err->message);
295           g_clear_error (&err);
296         }
297     }
298
299   g_free (utf8);
300
301   if ( filename == NULL)
302     filename = xstrdup (arg);
303
304   psppire_window_load (PSPPIRE_WINDOW (window), filename);
305
306   g_free (filename);
307 }
308
309 static void
310 handle_msg (const struct msg *m_, void *lexer_)
311 {
312   struct lexer *lexer = lexer_;
313   struct msg m = *m_;
314
315   if (lexer != NULL && m.file_name == NULL)
316     {
317       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
318       m.first_line = lex_get_first_line_number (lexer, 0);
319       m.last_line = lex_get_last_line_number (lexer, 0);
320       m.first_column = lex_get_first_column (lexer, 0);
321       m.last_column = lex_get_last_column (lexer, 0);
322     }
323
324   message_item_submit (message_item_create (&m));
325 }
326
327 void
328 psppire_set_lexer (struct lexer *lexer)
329 {
330   msg_set_handler (handle_msg, lexer);
331 }