src/data/any-reader.c (any_reader_may_open): Return tri-state value.
[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       enum detect_result res = any_reader_may_open (filename);
109
110       /* Check to see if the file is a .sav or a .por file.  If not
111          assume that it is a syntax file */
112       if (res == ANY_YES)
113         open_data_window (NULL, filename, NULL);
114       else if (res == ANY_NO)
115         {
116           create_data_window ();
117           open_syntax_window (filename, NULL);
118         }
119
120       g_free (filename);
121     }
122   else
123     create_data_window ();
124 }
125
126
127 void
128 de_initialize (void)
129 {
130   settings_done ();
131   output_close ();
132   i18n_done ();
133 }
134
135 static void
136 func (gpointer key, gpointer value, gpointer data)
137 {
138   gboolean rv;
139   PsppireWindow *window = PSPPIRE_WINDOW (value);
140
141   g_signal_emit_by_name (window, "delete-event", 0, &rv);
142 }
143
144 void
145 psppire_quit (void)
146 {
147   PsppireWindowRegister *reg = psppire_window_register_new ();
148   psppire_window_register_foreach (reg, func, NULL);
149
150   gtk_main_quit ();
151 }
152
153 static void
154 inject_renamed_icon (const char *icon, const char *substitute)
155 {
156   GtkIconTheme *theme = gtk_icon_theme_get_default ();
157   if (!gtk_icon_theme_has_icon (theme, icon)
158       && gtk_icon_theme_has_icon (theme, substitute))
159     {
160       gint *sizes = gtk_icon_theme_get_icon_sizes (theme, substitute);
161       gint *p;
162
163       for (p = sizes; *p != 0; p++)
164         {
165           gint size = *p;
166           GdkPixbuf *pb;
167
168           pb = gtk_icon_theme_load_icon (theme, substitute, size, 0, NULL);
169           if (pb != NULL)
170             {
171               GdkPixbuf *copy = gdk_pixbuf_copy (pb);
172               if (copy != NULL)
173                 gtk_icon_theme_add_builtin_icon (icon, size, copy);
174             }
175         }
176     }
177 }
178
179 /* Avoid a bug in GTK+ 2.22 that can cause a segfault at startup time.  Earlier
180    and later versions of GTK+ do not have the bug.  Bug #31511.
181
182    Based on this patch against Inkscape:
183    https://launchpadlibrarian.net/60175914/copy_renamed_icons.patch */
184 static void
185 inject_renamed_icons (void)
186 {
187   if (gtk_major_version == 2 && gtk_minor_version == 22)
188     {
189       inject_renamed_icon ("gtk-file", "document-x-generic");
190       inject_renamed_icon ("gtk-directory", "folder");
191     }
192 }
193
194
195 struct icon_size
196 {
197   int resolution;  /* The dimension of the images which will be used */
198   size_t n_sizes;  /* The number of items in the array below.
199                       This may be zero, in which case the iconset will be wildcarded
200                       (used by default when no non-wildcarded set is available) */
201   const GtkIconSize *usage; /* An array determining for what the icon set is used */
202 };
203
204 static const GtkIconSize menus[] = {GTK_ICON_SIZE_MENU};
205 static const GtkIconSize toolbar[] = {GTK_ICON_SIZE_LARGE_TOOLBAR};
206
207
208 /* We currently have three icon sets viz: 16x16, 24x24 and 32x32
209    We use the 16x16 for menus, the 32x32 for the toolbar and 
210    the 24x24 for everything else.
211
212    Exactly one element of the following array should have its 2nd and 3rd
213    argument as zero.
214 */
215 static const struct icon_size sizemap[] = 
216 {
217   {16,  sizeof (menus) / sizeof (GtkIconSize), menus},
218   {24, 0, 0},
219   {32,  sizeof (toolbar) / sizeof (GtkIconSize), toolbar}
220 };
221
222
223 static void
224 create_icon_factory (void)
225 {
226   gint c;
227   GtkIconFactory *factory = gtk_icon_factory_new ();
228   struct icon_context ctx[2];
229   ctx[0] = action_icon_context;
230   ctx[1] = category_icon_context;
231   for (c = 0 ; c < 2 ; ++c)
232   {
233     const struct icon_context *ic = &ctx[c];
234     gint i;
235     for (i = 0 ; i < ic->n_icons ; ++i)
236       {
237         GtkIconSet *icon_set = gtk_icon_set_new ();
238         int r;
239         for (r = 0 ; r < sizeof (sizemap) / sizeof (sizemap[0]); ++r)
240           {
241             int s;
242             GtkIconSource *source = gtk_icon_source_new ();
243             gchar *filename = g_strdup_printf ("%s/%s/%dx%d/%s.png", PKGDATADIR,
244                                                ic->context_name,
245                                                sizemap[r].resolution, sizemap[r].resolution,
246                                                ic->icon_name[i]);
247             const char *relocated_filename = relocate (filename);
248
249             gtk_icon_source_set_filename (source, relocated_filename);
250             gtk_icon_source_set_size_wildcarded (source, sizemap[r].n_sizes <= 0);
251             for (s = 0 ; s < sizemap[r].n_sizes ; ++s)
252               gtk_icon_source_set_size (source, sizemap[r].usage[s]);
253             if (filename != relocated_filename)
254               free (CONST_CAST (char *, relocated_filename));
255             g_free (filename);
256             gtk_icon_set_add_source (icon_set, source);
257           }
258       
259         gtk_icon_factory_add (factory, ic->icon_name[i], icon_set);
260     }
261   }
262
263   {
264     struct iconmap
265     {
266       const gchar *gtk_id;
267       gchar *pspp_id;
268     };
269
270     /* We have our own icons for some things.
271        But we want the Stock Item to be identical to the Gtk standard
272        ones in all other respects.
273     */
274     const struct iconmap map[] = {
275       {GTK_STOCK_NEW,    "file-new-document"},
276       {GTK_STOCK_QUIT,   "file-quit"},
277       {GTK_STOCK_SAVE,   "file-save-document"},
278       {GTK_STOCK_CUT,    "edit-cut"},
279       {GTK_STOCK_COPY,   "edit-copy"},
280       {GTK_STOCK_PASTE,  "edit-paste"},
281       {GTK_STOCK_UNDO,   "edit-undo"},
282       {GTK_STOCK_REDO,   "edit-redo"},
283       {GTK_STOCK_DELETE, "edit-delete"},
284       {GTK_STOCK_ABOUT,  "help-about"},
285       {GTK_STOCK_PRINT,  "file-print-document"}
286     };
287
288     GtkStockItem customised[sizeof (map) / sizeof (map[0])];
289     int i;
290
291     for (i = 0; i < sizeof (map) / sizeof (map[0]); ++i)
292     {
293       gtk_stock_lookup (map[i].gtk_id, &customised[i]);
294       customised[i].stock_id =  map[i].pspp_id;
295     }
296
297
298
299     gtk_stock_add (customised, sizeof (map) / sizeof (map[0]));
300   }
301
302   {
303     /* Create our own "pspp-stock-reset" item, using the
304        GTK_STOCK_REFRESH icon set */
305     GtkStockItem items[2] = {
306       {"pspp-stock-reset", N_("_Reset"), 0, 0, PACKAGE},
307       {"pspp-stock-select", N_("_Select"), 0, 0, PACKAGE}
308     };
309
310     gtk_stock_add (items, 2);
311
312     gtk_icon_factory_add (factory, "pspp-stock-reset",
313                           gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
314                           );
315
316     gtk_icon_factory_add (factory, "pspp-stock-select",
317                           gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
318                           );
319   }
320
321   gtk_icon_factory_add_default (factory);
322 }
323 \f
324 /* 
325    Convert a filename from the local encoding into "filename" encoding.
326    The return value will be allocated on the heap.  It is the responsibility
327    of the caller to free it.
328  */
329 static gchar *
330 local_to_filename_encoding (const char *fn)
331 {
332   gchar *filename = NULL;
333   gchar *utf8 = NULL;
334   const gchar *local_encoding = NULL;
335   gsize written = -1;
336   const gboolean local_is_utf8 = g_get_charset (&local_encoding);
337
338   /* There seems to be no Glib function to convert from local encoding
339      to filename encoding.  Therefore it has to be done in two steps:
340      the intermediate encoding is UTF8.
341
342      Either step could fail.  However, in many cases the file can still
343      be loaded even if the conversion fails. So in those cases, after showing
344      a warning, we simply copy the locally encoded filename to the destination
345      and hope for the best.
346   */
347
348   if ( local_is_utf8)
349     {
350       utf8 = xstrdup (fn);
351     }
352   else
353     {
354       GError *err = NULL;
355       utf8 = g_locale_to_utf8 (fn, -1, NULL, &written, &err);
356       if ( NULL == utf8)
357         {
358           g_warning ("Cannot convert filename from local encoding `%s' to UTF-8: %s",
359                      local_encoding,
360                      err->message);
361           g_clear_error (&err);
362         }
363     }
364
365   if ( NULL != utf8)
366     {
367       GError *err = NULL;
368       filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
369       if ( NULL == filename)
370         {
371           g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
372                      err->message);
373           g_clear_error (&err);
374         }
375     }
376
377   g_free (utf8);
378
379   if ( filename == NULL)
380     filename = xstrdup (fn);
381
382   return filename;
383 }
384
385 static void
386 handle_msg (const struct msg *m_, void *lexer_)
387 {
388   struct lexer *lexer = lexer_;
389   struct msg m = *m_;
390
391   if (lexer != NULL && m.file_name == NULL)
392     {
393       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
394       m.first_line = lex_get_first_line_number (lexer, 0);
395       m.last_line = lex_get_last_line_number (lexer, 0);
396       m.first_column = lex_get_first_column (lexer, 0);
397       m.last_column = lex_get_last_column (lexer, 0);
398     }
399
400   message_item_submit (message_item_create (&m));
401 }
402
403 void
404 psppire_set_lexer (struct lexer *lexer)
405 {
406   msg_set_handler (handle_msg, lexer);
407 }