CORRELATIONS: Fixed bug displaying non-sqaure correlation matrices
[pspp] / src / ui / gui / psppire.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2004, 2005, 2006, 2009, 2010, 2011  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 #include <assert.h>
20 #include <gsl/gsl_errno.h>
21 #include <gtk/gtk.h>
22 #include <libintl.h>
23 #include <unistd.h>
24
25 #include "data/any-reader.h"
26 #include "data/casereader.h"
27 #include "data/dataset.h"
28 #include "data/datasheet.h"
29 #include "data/file-handle-def.h"
30 #include "data/file-name.h"
31 #include "data/por-file-reader.h"
32 #include "data/session.h"
33 #include "data/settings.h"
34 #include "data/sys-file-reader.h"
35
36 #include "language/lexer/lexer.h"
37 #include "libpspp/i18n.h"
38 #include "libpspp/message.h"
39 #include "libpspp/version.h"
40
41 #include "output/driver.h"
42 #include "output/journal.h"
43 #include "output/message-item.h"
44
45 #include "ui/gui/dict-display.h"
46 #include "ui/gui/executor.h"
47 #include "ui/gui/psppire-data-store.h"
48 #include "ui/gui/psppire-data-window.h"
49 #include "ui/gui/psppire-dict.h"
50 #include "ui/gui/psppire.h"
51 #include "ui/gui/psppire-output-window.h"
52 #include "ui/gui/psppire-syntax-window.h"
53 #include "ui/gui/psppire-selector.h"
54 #include "ui/gui/psppire-var-store.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 "gl/configmake.h"
62 #include "gl/xalloc.h"
63 #include "gl/relocatable.h"
64
65 static void inject_renamed_icons (void);
66 static void create_icon_factory (void);
67 static gchar *local_to_filename_encoding (const char *fn);
68
69
70 #define _(msgid) gettext (msgid)
71 #define N_(msgid) msgid
72
73
74 void
75 initialize (const char *data_file)
76 {
77   PsppireDataWindow *data_window;
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   inject_renamed_icons ();
92   create_icon_factory ();
93
94   psppire_output_window_setup ();
95
96   journal_enable ();
97   textdomain (PACKAGE);
98
99   psppire_selector_set_default_selection_func (GTK_TYPE_ENTRY, insert_source_row_into_entry);
100   psppire_selector_set_default_selection_func (PSPPIRE_VAR_VIEW_TYPE, insert_source_row_into_tree_view);
101   psppire_selector_set_default_selection_func (GTK_TYPE_TREE_VIEW, insert_source_row_into_tree_view);
102
103   data_window = psppire_default_data_window ();
104
105   if (data_file)
106     {
107       gchar *filename = local_to_filename_encoding (data_file);
108
109       /* Check to see if the file is a .sav or a .por file.  If not
110          assume that it is a syntax file */
111       if ( any_reader_may_open (filename))
112         psppire_window_load (PSPPIRE_WINDOW (data_window), filename);
113       else
114         open_syntax_window (filename, NULL);
115
116       g_free (filename);
117     }
118 }
119
120
121 void
122 de_initialize (void)
123 {
124   settings_done ();
125   output_close ();
126   i18n_done ();
127 }
128
129 static void
130 func (gpointer key, gpointer value, gpointer data)
131 {
132   gboolean rv;
133   PsppireWindow *window = PSPPIRE_WINDOW (value);
134
135   g_signal_emit_by_name (window, "delete-event", 0, &rv);
136 }
137
138 void
139 psppire_quit (void)
140 {
141   PsppireWindowRegister *reg = psppire_window_register_new ();
142   psppire_window_register_foreach (reg, func, NULL);
143
144   gtk_main_quit ();
145 }
146
147 static void
148 inject_renamed_icon (const char *icon, const char *substitute)
149 {
150   GtkIconTheme *theme = gtk_icon_theme_get_default ();
151   if (!gtk_icon_theme_has_icon (theme, icon)
152       && gtk_icon_theme_has_icon (theme, substitute))
153     {
154       gint *sizes = gtk_icon_theme_get_icon_sizes (theme, substitute);
155       gint *p;
156
157       for (p = sizes; *p != 0; p++)
158         {
159           gint size = *p;
160           GdkPixbuf *pb;
161
162           pb = gtk_icon_theme_load_icon (theme, substitute, size, 0, NULL);
163           if (pb != NULL)
164             {
165               GdkPixbuf *copy = gdk_pixbuf_copy (pb);
166               if (copy != NULL)
167                 gtk_icon_theme_add_builtin_icon (icon, size, copy);
168             }
169         }
170     }
171 }
172
173 /* Avoid a bug in GTK+ 2.22 that can cause a segfault at startup time.  Earlier
174    and later versions of GTK+ do not have the bug.  Bug #31511.
175
176    Based on this patch against Inkscape:
177    https://launchpadlibrarian.net/60175914/copy_renamed_icons.patch */
178 static void
179 inject_renamed_icons (void)
180 {
181   if (gtk_major_version == 2 && gtk_minor_version == 22)
182     {
183       inject_renamed_icon ("gtk-file", "document-x-generic");
184       inject_renamed_icon ("gtk-directory", "folder");
185     }
186 }
187
188 struct icon_info
189 {
190   const char *file_name;
191   const gchar *id;
192 };
193
194
195 static const struct icon_info icons[] =
196   {
197     {PKGDATADIR "/value-labels.png",    "pspp-value-labels"},
198     {PKGDATADIR "/weight-cases.png",    "pspp-weight-cases"},
199     {PKGDATADIR "/goto-variable.png",   "pspp-goto-variable"},
200     {PKGDATADIR "/insert-variable.png", "pspp-insert-variable"},
201     {PKGDATADIR "/insert-case.png",     "pspp-insert-case"},
202     {PKGDATADIR "/split-file.png",      "pspp-split-file"},
203     {PKGDATADIR "/select-cases.png",    "pspp-select-cases"},
204     {PKGDATADIR "/recent-dialogs.png",  "pspp-recent-dialogs"},
205     {PKGDATADIR "/nominal.png",         "var-nominal"},
206     {PKGDATADIR "/ordinal.png",         "var-ordinal"},
207     {PKGDATADIR "/scale.png",           "var-scale"},
208     {PKGDATADIR "/string.png",          "var-string"},
209     {PKGDATADIR "/date-scale.png",      "var-date-scale"}
210   };
211
212 static void
213 create_icon_factory (void)
214 {
215   gint i;
216   GtkIconFactory *factory = gtk_icon_factory_new ();
217
218   for (i = 0 ; i < sizeof (icons) / sizeof(icons[0]); ++i)
219     {
220       GError *err = NULL;
221       GdkPixbuf *pixbuf =
222         gdk_pixbuf_new_from_file (relocate (icons[i].file_name), &err);
223
224       if ( pixbuf )
225         {
226           GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
227           g_object_unref (pixbuf);
228           gtk_icon_factory_add ( factory, icons[i].id, icon_set);
229         }
230       else
231         {
232           g_warning ("Cannot create icon: %s", err->message);
233           g_clear_error (&err);
234         }
235     }
236
237   {
238     /* Create our own "pspp-stock-reset" item, using the
239        GTK_STOCK_REFRESH icon set */
240
241     GtkStockItem items[] = {
242       {"pspp-stock-reset", N_("_Reset"), 0, 0, PACKAGE},
243       {"pspp-stock-select", N_("_Select"), 0, 0, PACKAGE}
244     };
245
246
247     gtk_stock_add (items, 2);
248     gtk_icon_factory_add (factory, "pspp-stock-reset",
249                           gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
250                           );
251
252     gtk_icon_factory_add (factory, "pspp-stock-select",
253                           gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
254                           );
255   }
256
257   gtk_icon_factory_add_default (factory);
258 }
259 \f
260 /* 
261    Convert a filename from the local encoding into "filename" encoding.
262    The return value will be allocated on the heap.  It is the responsibility
263    of the caller to free it.
264  */
265 static gchar *
266 local_to_filename_encoding (const char *fn)
267 {
268   gchar *filename = NULL;
269   gchar *utf8 = NULL;
270   const gchar *local_encoding = NULL;
271   gsize written = -1;
272   const gboolean local_is_utf8 = g_get_charset (&local_encoding);
273
274   /* There seems to be no Glib function to convert from local encoding
275      to filename encoding.  Therefore it has to be done in two steps:
276      the intermediate encoding is UTF8.
277
278      Either step could fail.  However, in many cases the file can still
279      be loaded even if the conversion fails. So in those cases, after showing
280      a warning, we simply copy the locally encoded filename to the destination
281      and hope for the best.
282   */
283
284   if ( local_is_utf8)
285     {
286       utf8 = xstrdup (fn);
287     }
288   else
289     {
290       GError *err = NULL;
291       utf8 = g_locale_to_utf8 (fn, -1, NULL, &written, &err);
292       if ( NULL == utf8)
293         {
294           g_warning ("Cannot convert filename from local encoding `%s' to UTF-8: %s",
295                      local_encoding,
296                      err->message);
297           g_clear_error (&err);
298         }
299     }
300
301   if ( NULL != utf8)
302     {
303       GError *err = NULL;
304       filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
305       if ( NULL == filename)
306         {
307           g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
308                      err->message);
309           g_clear_error (&err);
310         }
311     }
312
313   g_free (utf8);
314
315   if ( filename == NULL)
316     filename = xstrdup (fn);
317
318   return filename;
319 }
320
321 static void
322 handle_msg (const struct msg *m_, void *lexer_)
323 {
324   struct lexer *lexer = lexer_;
325   struct msg m = *m_;
326
327   if (lexer != NULL && m.file_name == NULL)
328     {
329       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
330       m.first_line = lex_get_first_line_number (lexer, 0);
331       m.last_line = lex_get_last_line_number (lexer, 0);
332       m.first_column = lex_get_first_column (lexer, 0);
333       m.last_column = lex_get_last_column (lexer, 0);
334     }
335
336   message_item_submit (message_item_create (&m));
337 }
338
339 void
340 psppire_set_lexer (struct lexer *lexer)
341 {
342   msg_set_handler (handle_msg, lexer);
343 }