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