Fix bug when searching through an empty dataset.
[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/session.h"
33 #include "data/settings.h"
34
35 #include "language/lexer/lexer.h"
36 #include "libpspp/i18n.h"
37 #include "libpspp/message.h"
38 #include "libpspp/version.h"
39
40 #include "output/driver.h"
41 #include "output/journal.h"
42 #include "output/message-item.h"
43
44 #include "ui/gui/dict-display.h"
45 #include "ui/gui/executor.h"
46 #include "ui/gui/psppire-data-store.h"
47 #include "ui/gui/psppire-data-window.h"
48 #include "ui/gui/psppire-dict.h"
49 #include "ui/gui/psppire.h"
50 #include "ui/gui/psppire-output-window.h"
51 #include "ui/gui/psppire-syntax-window.h"
52 #include "ui/gui/psppire-selector.h"
53 #include "ui/gui/psppire-var-view.h"
54 #include "ui/gui/psppire-means-layer.h"
55 #include "ui/gui/psppire-window-register.h"
56 #include "ui/gui/widgets.h"
57 #include "ui/source-init-opts.h"
58 #include "ui/syntax-gen.h"
59
60 #include "ui/gui/icons/icon-names.h"
61
62
63 #include "gl/configmake.h"
64 #include "gl/xalloc.h"
65 #include "gl/relocatable.h"
66
67 static void create_icon_factory (void);
68 static gchar *local_to_filename_encoding (const char *fn);
69
70
71 #define _(msgid) gettext (msgid)
72 #define N_(msgid) msgid
73
74
75 void
76 initialize (const char *data_file)
77 {
78   i18n_init ();
79
80   preregister_widgets ();
81
82   gsl_set_error_handler_off ();
83   output_engine_push ();
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   /* FIXME: This should be implemented with a GtkInterface */
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   psppire_selector_set_default_selection_func (PSPPIRE_TYPE_MEANS_LAYER, insert_source_row_into_layers);
103
104   if (data_file)
105     {
106       gchar *filename = local_to_filename_encoding (data_file);
107
108       int retval = any_reader_detect (filename, NULL);
109
110       /* Check to see if the file is a .sav or a .por file.  If not
111          assume that it is a syntax file */
112       if (retval == 1)
113         open_data_window (NULL, filename, NULL, NULL);
114       else if (retval == 0)
115         {
116           create_data_window ();
117           open_syntax_window (filename, NULL);
118         }
119
120       g_free (filename);
121     }
122   else
123     create_data_window ();
124 }
125
126
127 void
128 de_initialize (void)
129 {
130   settings_done ();
131   output_engine_pop ();
132   i18n_done ();
133 }
134
135 static void
136 func (gpointer key, gpointer value, gpointer data)
137 {
138   gboolean rv;
139   PsppireWindow *window = PSPPIRE_WINDOW (value);
140
141   g_signal_emit_by_name (window, "delete-event", 0, &rv);
142 }
143
144 void
145 psppire_quit (void)
146 {
147   PsppireWindowRegister *reg = psppire_window_register_new ();
148   psppire_window_register_foreach (reg, func, NULL);
149
150   gtk_main_quit ();
151 }
152
153 struct icon_size
154 {
155   int resolution;  /* The dimension of the images which will be used */
156   size_t n_sizes;  /* The number of items in the array below. */
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 large_toolbar[] = {GTK_ICON_SIZE_LARGE_TOOLBAR};
162 static const GtkIconSize small_toolbar[] = {GTK_ICON_SIZE_SMALL_TOOLBAR};
163
164
165 /* We currently have three icon sets viz: 16x16, 24x24 and 32x32
166    We use the 16x16 for menus, the 32x32 for the large_toolbars and 
167    the 24x24 for small_toolbars.
168
169    The order of this array is pertinent.  The icons in the sets occuring
170    earlier in the array will be used a the wildcard (default) icon size,
171    if such an icon exists.
172 */
173 static const struct icon_size sizemap[] = 
174 {
175   {24,  sizeof (small_toolbar) / sizeof (GtkIconSize), small_toolbar},
176   {16,  sizeof (menus) / sizeof (GtkIconSize), menus},
177   {32,  sizeof (large_toolbar) / sizeof (GtkIconSize), large_toolbar}
178 };
179
180
181 static void
182 create_icon_factory (void)
183 {
184   gint c;
185   GtkIconFactory *factory = gtk_icon_factory_new ();
186   struct icon_context ctx[2];
187   ctx[0] = action_icon_context;
188   ctx[1] = category_icon_context;
189   for (c = 0 ; c < 2 ; ++c)
190   {
191     const struct icon_context *ic = &ctx[c];
192     gint i;
193     for (i = 0 ; i < ic->n_icons ; ++i)
194       {
195         gboolean wildcarded = FALSE;
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             GFile *gf = g_file_new_for_path (relocated_filename);
208             if (g_file_query_exists (gf, NULL))
209               {
210                 gtk_icon_source_set_filename (source, relocated_filename);
211                 if (!wildcarded)
212                   {
213                     gtk_icon_source_set_size_wildcarded (source, TRUE);
214                     wildcarded = TRUE;
215                   }
216               }
217             g_object_unref (gf);
218
219             for (s = 0 ; s < sizemap[r].n_sizes ; ++s)
220               gtk_icon_source_set_size (source, sizemap[r].usage[s]);
221             if (filename != relocated_filename)
222               free (CONST_CAST (char *, relocated_filename));
223             g_free (filename);
224
225             if ( gtk_icon_source_get_filename (source))
226               gtk_icon_set_add_source (icon_set, source);
227           }
228       
229         gtk_icon_factory_add (factory, ic->icon_name[i], icon_set);
230     }
231   }
232
233   {
234     struct iconmap
235     {
236       const gchar *gtk_id;
237       gchar *pspp_id;
238     };
239
240     /* We have our own icons for some things.
241        But we want the Stock Item to be identical to the Gtk standard
242        ones in all other respects.
243     */
244     const struct iconmap map[] = {
245       {GTK_STOCK_NEW,    "file-new-document"},
246       {GTK_STOCK_QUIT,   "file-quit"},
247       {GTK_STOCK_SAVE,   "file-save-document"},
248       {GTK_STOCK_CUT,    "edit-cut"},
249       {GTK_STOCK_COPY,   "edit-copy"},
250       {GTK_STOCK_PASTE,  "edit-paste"},
251       {GTK_STOCK_UNDO,   "edit-undo"},
252       {GTK_STOCK_REDO,   "edit-redo"},
253       {GTK_STOCK_DELETE, "edit-delete"},
254       {GTK_STOCK_ABOUT,  "help-about"},
255       {GTK_STOCK_PRINT,  "file-print-document"}
256     };
257
258     GtkStockItem customised[sizeof (map) / sizeof (map[0])];
259     int i;
260
261     for (i = 0; i < sizeof (map) / sizeof (map[0]); ++i)
262     {
263       gtk_stock_lookup (map[i].gtk_id, &customised[i]);
264       customised[i].stock_id =  map[i].pspp_id;
265     }
266
267
268
269     gtk_stock_add (customised, sizeof (map) / sizeof (map[0]));
270   }
271
272   {
273     /* Create our own "pspp-stock-reset" item, using the
274        GTK_STOCK_REFRESH icon set */
275     GtkStockItem items[2] = {
276       {"pspp-stock-reset", N_("_Reset"), 0, 0, PACKAGE},
277       {"pspp-stock-select", N_("_Select"), 0, 0, PACKAGE}
278     };
279
280     gtk_stock_add (items, 2);
281
282     gtk_icon_factory_add (factory, "pspp-stock-reset",
283                           gtk_icon_factory_lookup_default (GTK_STOCK_REFRESH)
284                           );
285
286     gtk_icon_factory_add (factory, "pspp-stock-select",
287                           gtk_icon_factory_lookup_default (GTK_STOCK_INDEX)
288                           );
289   }
290
291   gtk_icon_factory_add_default (factory);
292 }
293 \f
294 /* 
295    Convert a filename from the local encoding into "filename" encoding.
296    The return value will be allocated on the heap.  It is the responsibility
297    of the caller to free it.
298  */
299 static gchar *
300 local_to_filename_encoding (const char *fn)
301 {
302   gchar *filename = NULL;
303   gchar *utf8 = NULL;
304   const gchar *local_encoding = NULL;
305   gsize written = -1;
306   const gboolean local_is_utf8 = g_get_charset (&local_encoding);
307
308   /* There seems to be no Glib function to convert from local encoding
309      to filename encoding.  Therefore it has to be done in two steps:
310      the intermediate encoding is UTF8.
311
312      Either step could fail.  However, in many cases the file can still
313      be loaded even if the conversion fails. So in those cases, after showing
314      a warning, we simply copy the locally encoded filename to the destination
315      and hope for the best.
316   */
317
318   if ( local_is_utf8)
319     {
320       utf8 = xstrdup (fn);
321     }
322   else
323     {
324       GError *err = NULL;
325       utf8 = g_locale_to_utf8 (fn, -1, NULL, &written, &err);
326       if ( NULL == utf8)
327         {
328           g_warning ("Cannot convert filename from local encoding `%s' to UTF-8: %s",
329                      local_encoding,
330                      err->message);
331           g_clear_error (&err);
332         }
333     }
334
335   if ( NULL != utf8)
336     {
337       GError *err = NULL;
338       filename = g_filename_from_utf8 (utf8, written, NULL, NULL, &err);
339       if ( NULL == filename)
340         {
341           g_warning ("Cannot convert filename from UTF8 to filename encoding: %s",
342                      err->message);
343           g_clear_error (&err);
344         }
345     }
346
347   g_free (utf8);
348
349   if ( filename == NULL)
350     filename = xstrdup (fn);
351
352   return filename;
353 }
354
355 static void
356 handle_msg (const struct msg *m_, void *lexer_)
357 {
358   struct lexer *lexer = lexer_;
359   struct msg m = *m_;
360
361   if (lexer != NULL && m.file_name == NULL)
362     {
363       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
364       m.first_line = lex_get_first_line_number (lexer, 0);
365       m.last_line = lex_get_last_line_number (lexer, 0);
366       m.first_column = lex_get_first_column (lexer, 0);
367       m.last_column = lex_get_last_column (lexer, 0);
368     }
369
370   message_item_submit (message_item_create (&m));
371 }
372
373 void
374 psppire_set_lexer (struct lexer *lexer)
375 {
376   msg_set_handler (handle_msg, lexer);
377 }