Merge 'master' into 'gtk3'.
[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_UNDO,   "edit-undo"},
280       {GTK_STOCK_REDO,   "edit-redo"},
281       {GTK_STOCK_DELETE, "edit-delete"},
282       {GTK_STOCK_ABOUT,  "help-about"},
283       {GTK_STOCK_PRINT,  "file-print-document"}
284     };
285
286     GtkStockItem customised[sizeof (map) / sizeof (map[0])];
287     int i;
288
289     for (i = 0; i < sizeof (map) / sizeof (map[0]); ++i)
290     {
291       gtk_stock_lookup (map[i].gtk_id, &customised[i]);
292       customised[i].stock_id =  map[i].pspp_id;
293     }
294
295
296
297     gtk_stock_add (customised, sizeof (map) / sizeof (map[0]));
298   }
299
300   {
301     /* Create our own "pspp-stock-reset" item, using the
302        GTK_STOCK_REFRESH icon set */
303     GtkStockItem items[2] = {
304       {"pspp-stock-reset", N_("_Reset"), 0, 0, PACKAGE},
305       {"pspp-stock-select", N_("_Select"), 0, 0, PACKAGE}
306     };
307
308     gtk_stock_add (items, 2);
309
310     gtk_icon_factory_add (factory, "pspp-stock-reset",
311                           gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
312                           );
313
314     gtk_icon_factory_add (factory, "pspp-stock-select",
315                           gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
316                           );
317   }
318
319   gtk_icon_factory_add_default (factory);
320 }
321 \f
322 /* 
323    Convert a filename from the local encoding into "filename" encoding.
324    The return value will be allocated on the heap.  It is the responsibility
325    of the caller to free it.
326  */
327 static gchar *
328 local_to_filename_encoding (const char *fn)
329 {
330   gchar *filename = NULL;
331   gchar *utf8 = NULL;
332   const gchar *local_encoding = NULL;
333   gsize written = -1;
334   const gboolean local_is_utf8 = g_get_charset (&local_encoding);
335
336   /* There seems to be no Glib function to convert from local encoding
337      to filename encoding.  Therefore it has to be done in two steps:
338      the intermediate encoding is UTF8.
339
340      Either step could fail.  However, in many cases the file can still
341      be loaded even if the conversion fails. So in those cases, after showing
342      a warning, we simply copy the locally encoded filename to the destination
343      and hope for the best.
344   */
345
346   if ( local_is_utf8)
347     {
348       utf8 = xstrdup (fn);
349     }
350   else
351     {
352       GError *err = NULL;
353       utf8 = g_locale_to_utf8 (fn, -1, NULL, &written, &err);
354       if ( NULL == utf8)
355         {
356           g_warning ("Cannot convert filename from local encoding `%s' to UTF-8: %s",
357                      local_encoding,
358                      err->message);
359           g_clear_error (&err);
360         }
361     }
362
363   if ( NULL != utf8)
364     {
365       GError *err = NULL;
366       filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
367       if ( NULL == filename)
368         {
369           g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
370                      err->message);
371           g_clear_error (&err);
372         }
373     }
374
375   g_free (utf8);
376
377   if ( filename == NULL)
378     filename = xstrdup (fn);
379
380   return filename;
381 }
382
383 static void
384 handle_msg (const struct msg *m_, void *lexer_)
385 {
386   struct lexer *lexer = lexer_;
387   struct msg m = *m_;
388
389   if (lexer != NULL && m.file_name == NULL)
390     {
391       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
392       m.first_line = lex_get_first_line_number (lexer, 0);
393       m.last_line = lex_get_last_line_number (lexer, 0);
394       m.first_column = lex_get_first_column (lexer, 0);
395       m.last_column = lex_get_last_column (lexer, 0);
396     }
397
398   message_item_submit (message_item_create (&m));
399 }
400
401 void
402 psppire_set_lexer (struct lexer *lexer)
403 {
404   msg_set_handler (handle_msg, lexer);
405 }