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