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