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