psppire: Relocate icon files also.
[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_enable ();
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 struct icon_size
193 {
194   int resolution;
195   GtkIconSize size;
196 };
197
198
199 static const struct icon_size sizes[] = {
200   {16,  GTK_ICON_SIZE_MENU},
201   {24,  GTK_ICON_SIZE_LARGE_TOOLBAR}
202 };
203
204
205 static void
206 create_icon_factory (void)
207 {
208   gint c;
209   GtkIconFactory *factory = gtk_icon_factory_new ();
210   struct icon_context xx[2];
211   xx[0] = action_icon_context;
212   xx[1] = category_icon_context;
213   for (c = 0 ; c < 2 ; ++c)
214   {
215     const struct icon_context *ic = &xx[c];
216     gint i;
217     for (i = 0 ; i < ic->n_icons ; ++i)
218       {
219         GtkIconSet *icon_set = gtk_icon_set_new ();
220         int r;
221         for (r = 0 ; r < sizeof (sizes) / sizeof (sizes[0]); ++r)
222           {
223             GtkIconSource *source = gtk_icon_source_new ();
224             gchar *filename = g_strdup_printf ("%s/%s/%dx%d/%s.png", PKGDATADIR,
225                                                ic->context_name,
226                                                sizes[r].resolution, sizes[r].resolution,
227                                                ic->icon_name[i]);
228             const char *relocated_filename = relocate (filename);
229
230             gtk_icon_source_set_filename (source, relocated_filename);
231             gtk_icon_source_set_size_wildcarded (source, FALSE);
232             gtk_icon_source_set_size (source, sizes[r].size);
233             if (filename != relocated_filename)
234               free (CONST_CAST (char *, relocated_filename));
235             g_free (filename);
236             gtk_icon_set_add_source (icon_set, source);
237           }
238       
239         gtk_icon_factory_add (factory, ic->icon_name[i], icon_set);
240     }
241   }
242
243   {
244     struct iconmap
245     {
246       const gchar *gtk_id;
247       gchar *pspp_id;
248     };
249
250     /* We have our own icons for some things.
251        But we want the Stock Item to be identical to the Gtk standard
252        ones in all other respects.
253     */
254     const struct iconmap map[] = {
255       {GTK_STOCK_NEW,   "file-new-document"},
256       {GTK_STOCK_QUIT,  "file-quit"},
257       {GTK_STOCK_SAVE,  "file-save-document"},
258       {GTK_STOCK_CUT,   "edit-cut"},
259       {GTK_STOCK_COPY,  "edit-copy"},
260       {GTK_STOCK_PASTE, "edit-paste"},
261       {GTK_STOCK_ABOUT, "help-about"},
262       {GTK_STOCK_PRINT, "file-print-document"}
263     };
264
265     GtkStockItem customised[sizeof (map) / sizeof (map[0])];
266     int i;
267
268     for (i = 0; i < sizeof (map) / sizeof (map[0]); ++i)
269     {
270       gtk_stock_lookup (map[i].gtk_id, &customised[i]);
271       customised[i].stock_id =  map[i].pspp_id;
272     }
273
274
275
276     gtk_stock_add (customised, sizeof (map) / sizeof (map[0]));
277   }
278
279   {
280     /* Create our own "pspp-stock-reset" item, using the
281        GTK_STOCK_REFRESH icon set */
282     GtkStockItem items[2] = {
283       {"pspp-stock-reset", N_("_Reset"), 0, 0, PACKAGE},
284       {"pspp-stock-select", N_("_Select"), 0, 0, PACKAGE}
285     };
286
287     gtk_stock_add (items, 2);
288
289     gtk_icon_factory_add (factory, "pspp-stock-reset",
290                           gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
291                           );
292
293     gtk_icon_factory_add (factory, "pspp-stock-select",
294                           gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
295                           );
296   }
297
298   gtk_icon_factory_add_default (factory);
299 }
300 \f
301 /* 
302    Convert a filename from the local encoding into "filename" encoding.
303    The return value will be allocated on the heap.  It is the responsibility
304    of the caller to free it.
305  */
306 static gchar *
307 local_to_filename_encoding (const char *fn)
308 {
309   gchar *filename = NULL;
310   gchar *utf8 = NULL;
311   const gchar *local_encoding = NULL;
312   gsize written = -1;
313   const gboolean local_is_utf8 = g_get_charset (&local_encoding);
314
315   /* There seems to be no Glib function to convert from local encoding
316      to filename encoding.  Therefore it has to be done in two steps:
317      the intermediate encoding is UTF8.
318
319      Either step could fail.  However, in many cases the file can still
320      be loaded even if the conversion fails. So in those cases, after showing
321      a warning, we simply copy the locally encoded filename to the destination
322      and hope for the best.
323   */
324
325   if ( local_is_utf8)
326     {
327       utf8 = xstrdup (fn);
328     }
329   else
330     {
331       GError *err = NULL;
332       utf8 = g_locale_to_utf8 (fn, -1, NULL, &written, &err);
333       if ( NULL == utf8)
334         {
335           g_warning ("Cannot convert filename from local encoding `%s' to UTF-8: %s",
336                      local_encoding,
337                      err->message);
338           g_clear_error (&err);
339         }
340     }
341
342   if ( NULL != utf8)
343     {
344       GError *err = NULL;
345       filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
346       if ( NULL == filename)
347         {
348           g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
349                      err->message);
350           g_clear_error (&err);
351         }
352     }
353
354   g_free (utf8);
355
356   if ( filename == NULL)
357     filename = xstrdup (fn);
358
359   return filename;
360 }
361
362 static void
363 handle_msg (const struct msg *m_, void *lexer_)
364 {
365   struct lexer *lexer = lexer_;
366   struct msg m = *m_;
367
368   if (lexer != NULL && m.file_name == NULL)
369     {
370       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
371       m.first_line = lex_get_first_line_number (lexer, 0);
372       m.last_line = lex_get_last_line_number (lexer, 0);
373       m.first_column = lex_get_first_column (lexer, 0);
374       m.last_column = lex_get_last_column (lexer, 0);
375     }
376
377   message_item_submit (message_item_create (&m));
378 }
379
380 void
381 psppire_set_lexer (struct lexer *lexer)
382 {
383   msg_set_handler (handle_msg, lexer);
384 }