93f3f99f7b4ca40941549ce248950472fc05fcbf
[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-means-layer.h"
57 #include "ui/gui/psppire-window-register.h"
58 #include "ui/gui/widgets.h"
59 #include "ui/source-init-opts.h"
60 #include "ui/syntax-gen.h"
61
62 #include "ui/gui/icons/icon-names.h"
63
64
65 #include "gl/configmake.h"
66 #include "gl/xalloc.h"
67 #include "gl/relocatable.h"
68
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   create_icon_factory ();
93
94   psppire_output_window_setup ();
95
96   journal_init ();
97   textdomain (PACKAGE);
98
99   /* FIXME: This should be implemented with a GtkInterface */
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   psppire_selector_set_default_selection_func (PSPPIRE_TYPE_MEANS_LAYER, insert_source_row_into_layers);
104
105   if (data_file)
106     {
107       gchar *filename = local_to_filename_encoding (data_file);
108
109       enum detect_result res = any_reader_may_open (filename);
110
111       /* Check to see if the file is a .sav or a .por file.  If not
112          assume that it is a syntax file */
113       if (res == ANY_YES)
114         open_data_window (NULL, filename, NULL, NULL);
115       else if (res == ANY_NO)
116         {
117           create_data_window ();
118           open_syntax_window (filename, NULL);
119         }
120
121       g_free (filename);
122     }
123   else
124     create_data_window ();
125 }
126
127
128 void
129 de_initialize (void)
130 {
131   settings_done ();
132   output_close ();
133   i18n_done ();
134 }
135
136 static void
137 func (gpointer key, gpointer value, gpointer data)
138 {
139   gboolean rv;
140   PsppireWindow *window = PSPPIRE_WINDOW (value);
141
142   g_signal_emit_by_name (window, "delete-event", 0, &rv);
143 }
144
145 void
146 psppire_quit (void)
147 {
148   PsppireWindowRegister *reg = psppire_window_register_new ();
149   psppire_window_register_foreach (reg, func, NULL);
150
151   gtk_main_quit ();
152 }
153
154 struct icon_size
155 {
156   int resolution;  /* The dimension of the images which will be used */
157   size_t n_sizes;  /* The number of items in the array below.
158                       This may be zero, in which case the iconset will be wildcarded
159                       (used by default when no non-wildcarded set is available) */
160   const GtkIconSize *usage; /* An array determining for what the icon set is used */
161 };
162
163 static const GtkIconSize menus[] = {GTK_ICON_SIZE_MENU};
164 static const GtkIconSize toolbar[] = {GTK_ICON_SIZE_LARGE_TOOLBAR};
165
166
167 /* We currently have three icon sets viz: 16x16, 24x24 and 32x32
168    We use the 16x16 for menus, the 32x32 for the toolbar and 
169    the 24x24 for everything else.
170
171    Exactly one element of the following array should have its 2nd and 3rd
172    argument as zero.
173 */
174 static const struct icon_size sizemap[] = 
175 {
176   {16,  sizeof (menus) / sizeof (GtkIconSize), menus},
177   {24, 0, 0},
178   {32,  sizeof (toolbar) / sizeof (GtkIconSize), toolbar}
179 };
180
181
182 static void
183 create_icon_factory (void)
184 {
185   gint c;
186   GtkIconFactory *factory = gtk_icon_factory_new ();
187   struct icon_context ctx[2];
188   ctx[0] = action_icon_context;
189   ctx[1] = category_icon_context;
190   for (c = 0 ; c < 2 ; ++c)
191   {
192     const struct icon_context *ic = &ctx[c];
193     gint i;
194     for (i = 0 ; i < ic->n_icons ; ++i)
195       {
196         GtkIconSet *icon_set = gtk_icon_set_new ();
197         int r;
198         for (r = 0 ; r < sizeof (sizemap) / sizeof (sizemap[0]); ++r)
199           {
200             int s;
201             GtkIconSource *source = gtk_icon_source_new ();
202             gchar *filename = g_strdup_printf ("%s/%s/%dx%d/%s.png", PKGDATADIR,
203                                                ic->context_name,
204                                                sizemap[r].resolution, sizemap[r].resolution,
205                                                ic->icon_name[i]);
206             const char *relocated_filename = relocate (filename);
207
208             gtk_icon_source_set_filename (source, relocated_filename);
209             gtk_icon_source_set_size_wildcarded (source, sizemap[r].n_sizes <= 0);
210             for (s = 0 ; s < sizemap[r].n_sizes ; ++s)
211               gtk_icon_source_set_size (source, sizemap[r].usage[s]);
212             if (filename != relocated_filename)
213               free (CONST_CAST (char *, relocated_filename));
214             g_free (filename);
215             gtk_icon_set_add_source (icon_set, source);
216           }
217       
218         gtk_icon_factory_add (factory, ic->icon_name[i], icon_set);
219     }
220   }
221
222   {
223     struct iconmap
224     {
225       const gchar *gtk_id;
226       gchar *pspp_id;
227     };
228
229     /* We have our own icons for some things.
230        But we want the Stock Item to be identical to the Gtk standard
231        ones in all other respects.
232     */
233     const struct iconmap map[] = {
234       {GTK_STOCK_NEW,    "file-new-document"},
235       {GTK_STOCK_QUIT,   "file-quit"},
236       {GTK_STOCK_SAVE,   "file-save-document"},
237       {GTK_STOCK_CUT,    "edit-cut"},
238       {GTK_STOCK_COPY,   "edit-copy"},
239       {GTK_STOCK_PASTE,  "edit-paste"},
240       {GTK_STOCK_UNDO,   "edit-undo"},
241       {GTK_STOCK_REDO,   "edit-redo"},
242       {GTK_STOCK_DELETE, "edit-delete"},
243       {GTK_STOCK_ABOUT,  "help-about"},
244       {GTK_STOCK_PRINT,  "file-print-document"}
245     };
246
247     GtkStockItem customised[sizeof (map) / sizeof (map[0])];
248     int i;
249
250     for (i = 0; i < sizeof (map) / sizeof (map[0]); ++i)
251     {
252       gtk_stock_lookup (map[i].gtk_id, &customised[i]);
253       customised[i].stock_id =  map[i].pspp_id;
254     }
255
256
257
258     gtk_stock_add (customised, sizeof (map) / sizeof (map[0]));
259   }
260
261   {
262     /* Create our own "pspp-stock-reset" item, using the
263        GTK_STOCK_REFRESH icon set */
264     GtkStockItem items[2] = {
265       {"pspp-stock-reset", N_("_Reset"), 0, 0, PACKAGE},
266       {"pspp-stock-select", N_("_Select"), 0, 0, PACKAGE}
267     };
268
269     gtk_stock_add (items, 2);
270
271     gtk_icon_factory_add (factory, "pspp-stock-reset",
272                           gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
273                           );
274
275     gtk_icon_factory_add (factory, "pspp-stock-select",
276                           gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
277                           );
278   }
279
280   gtk_icon_factory_add_default (factory);
281 }
282 \f
283 /* 
284    Convert a filename from the local encoding into "filename" encoding.
285    The return value will be allocated on the heap.  It is the responsibility
286    of the caller to free it.
287  */
288 static gchar *
289 local_to_filename_encoding (const char *fn)
290 {
291   gchar *filename = NULL;
292   gchar *utf8 = NULL;
293   const gchar *local_encoding = NULL;
294   gsize written = -1;
295   const gboolean local_is_utf8 = g_get_charset (&local_encoding);
296
297   /* There seems to be no Glib function to convert from local encoding
298      to filename encoding.  Therefore it has to be done in two steps:
299      the intermediate encoding is UTF8.
300
301      Either step could fail.  However, in many cases the file can still
302      be loaded even if the conversion fails. So in those cases, after showing
303      a warning, we simply copy the locally encoded filename to the destination
304      and hope for the best.
305   */
306
307   if ( local_is_utf8)
308     {
309       utf8 = xstrdup (fn);
310     }
311   else
312     {
313       GError *err = NULL;
314       utf8 = g_locale_to_utf8 (fn, -1, NULL, &written, &err);
315       if ( NULL == utf8)
316         {
317           g_warning ("Cannot convert filename from local encoding `%s' to UTF-8: %s",
318                      local_encoding,
319                      err->message);
320           g_clear_error (&err);
321         }
322     }
323
324   if ( NULL != utf8)
325     {
326       GError *err = NULL;
327       filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
328       if ( NULL == filename)
329         {
330           g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
331                      err->message);
332           g_clear_error (&err);
333         }
334     }
335
336   g_free (utf8);
337
338   if ( filename == NULL)
339     filename = xstrdup (fn);
340
341   return filename;
342 }
343
344 static void
345 handle_msg (const struct msg *m_, void *lexer_)
346 {
347   struct lexer *lexer = lexer_;
348   struct msg m = *m_;
349
350   if (lexer != NULL && m.file_name == NULL)
351     {
352       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
353       m.first_line = lex_get_first_line_number (lexer, 0);
354       m.last_line = lex_get_last_line_number (lexer, 0);
355       m.first_column = lex_get_first_column (lexer, 0);
356       m.last_column = lex_get_last_column (lexer, 0);
357     }
358
359   message_item_submit (message_item_create (&m));
360 }
361
362 void
363 psppire_set_lexer (struct lexer *lexer)
364 {
365   msg_set_handler (handle_msg, lexer);
366 }