a3f6a2aff67d9ebebafd0261c2d08ffb8968b96d
[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, 2014  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 create_icon_factory (void);
69 static gchar *local_to_filename_encoding (const char *fn);
70
71
72 #define _(msgid) gettext (msgid)
73 #define N_(msgid) msgid
74
75
76 void
77 initialize (const char *data_file)
78 {
79   i18n_init ();
80
81   preregister_widgets ();
82
83   gsl_set_error_handler_off ();
84   settings_init ();
85   fh_init ();
86
87   psppire_set_lexer (NULL);
88
89   bind_textdomain_codeset (PACKAGE, "UTF-8");
90
91   create_icon_factory ();
92
93   psppire_output_window_setup ();
94
95   journal_init ();
96   textdomain (PACKAGE);
97
98   psppire_selector_set_default_selection_func (GTK_TYPE_ENTRY, insert_source_row_into_entry);
99   psppire_selector_set_default_selection_func (PSPPIRE_VAR_VIEW_TYPE, insert_source_row_into_tree_view);
100   psppire_selector_set_default_selection_func (GTK_TYPE_TREE_VIEW, insert_source_row_into_tree_view);
101
102   if (data_file)
103     {
104       gchar *filename = local_to_filename_encoding (data_file);
105
106       enum detect_result res = any_reader_may_open (filename);
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 (res == ANY_YES)
111         open_data_window (NULL, filename, NULL, NULL);
112       else if (res == ANY_NO)
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 struct icon_size
152 {
153   int resolution;  /* The dimension of the images which will be used */
154   size_t n_sizes;  /* The number of items in the array below.
155                       This may be zero, in which case the iconset will be wildcarded
156                       (used by default when no non-wildcarded set is available) */
157   const GtkIconSize *usage; /* An array determining for what the icon set is used */
158 };
159
160 static const GtkIconSize menus[] = {GTK_ICON_SIZE_MENU};
161 static const GtkIconSize toolbar[] = {GTK_ICON_SIZE_LARGE_TOOLBAR};
162
163
164 /* We currently have three icon sets viz: 16x16, 24x24 and 32x32
165    We use the 16x16 for menus, the 32x32 for the toolbar and 
166    the 24x24 for everything else.
167
168    Exactly one element of the following array should have its 2nd and 3rd
169    argument as zero.
170 */
171 static const struct icon_size sizemap[] = 
172 {
173   {16,  sizeof (menus) / sizeof (GtkIconSize), menus},
174   {24, 0, 0},
175   {32,  sizeof (toolbar) / sizeof (GtkIconSize), toolbar}
176 };
177
178
179 static void
180 create_icon_factory (void)
181 {
182   gint c;
183   GtkIconFactory *factory = gtk_icon_factory_new ();
184   struct icon_context ctx[2];
185   ctx[0] = action_icon_context;
186   ctx[1] = category_icon_context;
187   for (c = 0 ; c < 2 ; ++c)
188   {
189     const struct icon_context *ic = &ctx[c];
190     gint i;
191     for (i = 0 ; i < ic->n_icons ; ++i)
192       {
193         GtkIconSet *icon_set = gtk_icon_set_new ();
194         int r;
195         for (r = 0 ; r < sizeof (sizemap) / sizeof (sizemap[0]); ++r)
196           {
197             int s;
198             GtkIconSource *source = gtk_icon_source_new ();
199             gchar *filename = g_strdup_printf ("%s/%s/%dx%d/%s.png", PKGDATADIR,
200                                                ic->context_name,
201                                                sizemap[r].resolution, sizemap[r].resolution,
202                                                ic->icon_name[i]);
203             const char *relocated_filename = relocate (filename);
204
205             gtk_icon_source_set_filename (source, relocated_filename);
206             gtk_icon_source_set_size_wildcarded (source, sizemap[r].n_sizes <= 0);
207             for (s = 0 ; s < sizemap[r].n_sizes ; ++s)
208               gtk_icon_source_set_size (source, sizemap[r].usage[s]);
209             if (filename != relocated_filename)
210               free (CONST_CAST (char *, relocated_filename));
211             g_free (filename);
212             gtk_icon_set_add_source (icon_set, source);
213           }
214       
215         gtk_icon_factory_add (factory, ic->icon_name[i], icon_set);
216     }
217   }
218
219   {
220     struct iconmap
221     {
222       const gchar *gtk_id;
223       gchar *pspp_id;
224     };
225
226     /* We have our own icons for some things.
227        But we want the Stock Item to be identical to the Gtk standard
228        ones in all other respects.
229     */
230     const struct iconmap map[] = {
231       {GTK_STOCK_NEW,    "file-new-document"},
232       {GTK_STOCK_QUIT,   "file-quit"},
233       {GTK_STOCK_SAVE,   "file-save-document"},
234       {GTK_STOCK_CUT,    "edit-cut"},
235       {GTK_STOCK_COPY,   "edit-copy"},
236       {GTK_STOCK_PASTE,  "edit-paste"},
237       {GTK_STOCK_UNDO,   "edit-undo"},
238       {GTK_STOCK_REDO,   "edit-redo"},
239       {GTK_STOCK_DELETE, "edit-delete"},
240       {GTK_STOCK_ABOUT,  "help-about"},
241       {GTK_STOCK_PRINT,  "file-print-document"}
242     };
243
244     GtkStockItem customised[sizeof (map) / sizeof (map[0])];
245     int i;
246
247     for (i = 0; i < sizeof (map) / sizeof (map[0]); ++i)
248     {
249       gtk_stock_lookup (map[i].gtk_id, &customised[i]);
250       customised[i].stock_id =  map[i].pspp_id;
251     }
252
253
254
255     gtk_stock_add (customised, sizeof (map) / sizeof (map[0]));
256   }
257
258   {
259     /* Create our own "pspp-stock-reset" item, using the
260        GTK_STOCK_REFRESH icon set */
261     GtkStockItem items[2] = {
262       {"pspp-stock-reset", N_("_Reset"), 0, 0, PACKAGE},
263       {"pspp-stock-select", N_("_Select"), 0, 0, PACKAGE}
264     };
265
266     gtk_stock_add (items, 2);
267
268     gtk_icon_factory_add (factory, "pspp-stock-reset",
269                           gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
270                           );
271
272     gtk_icon_factory_add (factory, "pspp-stock-select",
273                           gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
274                           );
275   }
276
277   gtk_icon_factory_add_default (factory);
278 }
279 \f
280 /* 
281    Convert a filename from the local encoding into "filename" encoding.
282    The return value will be allocated on the heap.  It is the responsibility
283    of the caller to free it.
284  */
285 static gchar *
286 local_to_filename_encoding (const char *fn)
287 {
288   gchar *filename = NULL;
289   gchar *utf8 = NULL;
290   const gchar *local_encoding = NULL;
291   gsize written = -1;
292   const gboolean local_is_utf8 = g_get_charset (&local_encoding);
293
294   /* There seems to be no Glib function to convert from local encoding
295      to filename encoding.  Therefore it has to be done in two steps:
296      the intermediate encoding is UTF8.
297
298      Either step could fail.  However, in many cases the file can still
299      be loaded even if the conversion fails. So in those cases, after showing
300      a warning, we simply copy the locally encoded filename to the destination
301      and hope for the best.
302   */
303
304   if ( local_is_utf8)
305     {
306       utf8 = xstrdup (fn);
307     }
308   else
309     {
310       GError *err = NULL;
311       utf8 = g_locale_to_utf8 (fn, -1, NULL, &written, &err);
312       if ( NULL == utf8)
313         {
314           g_warning ("Cannot convert filename from local encoding `%s' to UTF-8: %s",
315                      local_encoding,
316                      err->message);
317           g_clear_error (&err);
318         }
319     }
320
321   if ( NULL != utf8)
322     {
323       GError *err = NULL;
324       filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
325       if ( NULL == filename)
326         {
327           g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
328                      err->message);
329           g_clear_error (&err);
330         }
331     }
332
333   g_free (utf8);
334
335   if ( filename == NULL)
336     filename = xstrdup (fn);
337
338   return filename;
339 }
340
341 static void
342 handle_msg (const struct msg *m_, void *lexer_)
343 {
344   struct lexer *lexer = lexer_;
345   struct msg m = *m_;
346
347   if (lexer != NULL && m.file_name == NULL)
348     {
349       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
350       m.first_line = lex_get_first_line_number (lexer, 0);
351       m.last_line = lex_get_last_line_number (lexer, 0);
352       m.first_column = lex_get_first_column (lexer, 0);
353       m.last_column = lex_get_last_column (lexer, 0);
354     }
355
356   message_item_submit (message_item_create (&m));
357 }
358
359 void
360 psppire_set_lexer (struct lexer *lexer)
361 {
362   msg_set_handler (handle_msg, lexer);
363 }