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