Convert filename encodings when opening files.
[pspp-builds.git] / src / ui / gui / helper.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007  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
18 /* This file is a rubbish bin where stuff gets put when it doesn't seem to
19    belong anywhere else.
20 */
21 #include <config.h>
22
23 #include "psppire-syntax-window.h"
24
25 #include        <glib-object.h>
26
27 #include <glib.h>
28 #include "helper.h"
29 #include "message-dialog.h"
30 #include <data/format.h>
31 #include <data/data-in.h>
32 #include <data/data-out.h>
33 #include <data/dictionary.h>
34 #include <data/casereader-provider.h>
35 #include <libpspp/message.h>
36
37 #include <gtk/gtkbuilder.h>
38 #include <libpspp/i18n.h>
39
40 #include <ctype.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <data/settings.h>
44
45 #include <language/command.h>
46 #include <data/lazy-casereader.h>
47 #include <data/procedure.h>
48 #include <language/lexer/lexer.h>
49 #include "psppire-data-store.h"
50 #include <output/manager.h>
51 #include "psppire-output-window.h"
52
53 #include "xalloc.h"
54
55 #include <gettext.h>
56
57 /* Formats a value according to FORMAT
58    The returned string must be freed when no longer required */
59 gchar *
60 value_to_text (union value v, struct fmt_spec format)
61 {
62   gchar *s = 0;
63
64   s = g_new (gchar, format.w + 1);
65   data_out (&v, &format, s);
66   s[format.w]='\0';
67   g_strchug (s);
68
69   return s;
70 }
71
72
73
74 gboolean
75 text_to_value (const gchar *text, union value *v,
76               struct fmt_spec format)
77 {
78   bool ok;
79
80   if ( format.type != FMT_A)
81     {
82       if ( ! text ) return FALSE;
83
84       {
85         const gchar *s = text;
86         while (*s)
87           {
88             if ( !isspace (*s))
89               break;
90             s++;
91           }
92
93         if ( !*s) return FALSE;
94       }
95     }
96
97   msg_disable ();
98   ok = data_in (ss_cstr (text), LEGACY_NATIVE, format.type, 0, 0, 0,
99                 v, fmt_var_width (&format));
100   msg_enable ();
101
102   return ok;
103 }
104
105
106 GtkBuilder *
107 builder_new_real (const gchar *name)
108 {
109   GtkBuilder *builder = gtk_builder_new ();
110
111   GError *err = NULL;
112   if ( ! gtk_builder_add_from_file (builder, name,  &err))
113     {
114       g_critical ("Couldnt open user interface  file %s: %s", name, err->message);
115       g_clear_error (&err);
116     }
117
118   return builder;
119 }
120
121
122 GObject *
123 get_object_assert (GtkBuilder *builder, const gchar *name, GType type)
124 {
125   GObject *o = NULL;
126   g_assert (name);
127
128   o = gtk_builder_get_object (builder, name);
129
130   if ( !o )
131     g_critical ("Object \"%s\" could not be found\n", name);
132
133   if ( ! g_type_is_a (G_OBJECT_TYPE (o), type))
134    {
135      g_critical ("Object \"%s\" was expected to have type %s, but in fact has type %s", 
136         name, g_type_name (type), G_OBJECT_TYPE_NAME (o));
137    }
138
139   return o;
140 }
141
142
143 GtkAction *
144 get_action_assert (GtkBuilder *builder, const gchar *name)
145 {
146   return GTK_ACTION (get_object_assert (builder, name, GTK_TYPE_ACTION));
147 }
148
149 GtkWidget *
150 get_widget_assert (GtkBuilder *builder, const gchar *name)
151 {
152   return GTK_WIDGET (get_object_assert (builder, name, GTK_TYPE_WIDGET));
153 }
154
155 /* Converts a string in the pspp locale to utf-8.
156    The return value must be freed when no longer required*/
157 gchar *
158 pspp_locale_to_utf8 (const gchar *text, gssize len, GError **err)
159 {
160   return recode_string (CONV_PSPP_TO_UTF8, text, len);
161 }
162
163 gchar *
164 utf8_to_pspp_locale (const gchar *text, gssize len, GError **err)
165 {
166   return recode_string (CONV_UTF8_TO_PSPP, text, len);
167 }
168
169 /* This function must be used whenever a filename generated by glib,
170    (eg, from gtk_file_chooser_get_filename) and passed to the C library,
171    (eg through a pspp syntax string).
172 */
173 gchar *
174 convert_glib_filename_to_system_filename (const gchar *fname, GError **err)
175 {
176   gchar *output_name;
177
178 #ifdef G_OS_WIN32
179   const gchar *target_encoding;
180   gchar *utf8_name = NULL;
181
182   g_get_charset (&target_encoding);
183
184   output_name = g_convert (fname, -1, target_encoding,
185                         "UTF-8", NULL, NULL, err);
186 #else
187   output_name = strdup (fname);
188 #endif
189
190   return output_name;
191 }
192
193
194
195 #define _(msgid) gettext (msgid)
196 #define N_(msgid) msgid
197
198
199 static void
200 give_help (void)
201 {
202   GtkWidget *dialog;
203
204   dialog = gtk_message_dialog_new (NULL,
205                                    GTK_DIALOG_MODAL,
206                                    GTK_MESSAGE_INFO,
207                                    GTK_BUTTONS_CLOSE,
208                                    _("Sorry. The help system hasn't yet "
209                                      "been implemented."));
210   gtk_dialog_run (GTK_DIALOG (dialog));
211   gtk_widget_destroy (dialog);
212 }
213
214 void
215 connect_help (GtkBuilder *xml)
216 {
217   GSList *helps = gtk_builder_get_objects (xml);
218
219   GSList *i;
220   for ( i = helps; i ; i = g_slist_next (i))
221     {
222       GObject *o = i->data;
223       if ( GTK_IS_WIDGET (o) )
224         {
225           gchar *name = NULL;
226           gchar s[12] = {0};
227           g_object_get (o, "name", &name, NULL);
228
229           if ( name)
230             strncpy (s, name, 11);
231           s[11] = '\0';
232
233
234           if ( 0 == strcmp ("help_button", s))
235             {
236             g_signal_connect (o, "clicked", give_help, 0);
237             }
238         }
239     }
240
241   g_slist_free (helps);
242 }
243
244
245 void
246 reference_manual (GtkMenuItem *menu, gpointer data)
247 {
248   GError *err = NULL;
249   gchar *cmd = g_strdup_printf ("yelp file://%s", relocate (DOCDIR "/pspp.xml"));
250
251   if ( ! g_spawn_command_line_async (cmd, &err) )
252     {
253       msg (ME, _("Cannot open reference manual: %s"), err->message);
254     }
255
256   g_free (cmd);
257   g_clear_error (&err);
258 }
259
260
261 extern struct dataset *the_dataset;
262 extern struct source_stream *the_source_stream;
263 extern PsppireDataStore *the_data_store;
264
265 /* Lazy casereader callback function used by execute_syntax. */
266 static struct casereader *
267 create_casereader_from_data_store (void *data_store_)
268 {
269   PsppireDataStore *data_store = data_store_;
270   return psppire_data_store_get_reader (data_store);
271 }
272
273 gboolean
274 execute_syntax (struct getl_interface *sss)
275 {
276   struct lexer *lexer;
277   gboolean retval = TRUE;
278
279   struct casereader *reader;
280   size_t value_cnt;
281   casenumber case_cnt;
282   unsigned long int lazy_serial;
283
284   /* When the user executes a number of snippets of syntax in a
285      row, none of which read from the active file, the GUI becomes
286      progressively less responsive.  The reason is that each syntax
287      execution encapsulates the active file data in another
288      datasheet layer.  The cumulative effect of having a number of
289      layers of datasheets wastes time and space.
290
291      To solve the problem, we use a "lazy casereader", a wrapper
292      around the casereader obtained from the data store, that
293      only actually instantiates that casereader when it is
294      needed.  If the data store casereader is never needed, then
295      it is reused the next time syntax is run, without wrapping
296      it in another layer. */
297   value_cnt = psppire_data_store_get_value_count (the_data_store);
298   case_cnt = psppire_data_store_get_case_count (the_data_store);
299   reader = lazy_casereader_create (value_cnt, case_cnt,
300                                    create_casereader_from_data_store,
301                                    the_data_store, &lazy_serial);
302   proc_set_active_file_data (the_dataset, reader);
303
304   g_return_val_if_fail (proc_has_active_file (the_dataset), FALSE);
305
306   lexer = lex_create (the_source_stream);
307
308   getl_append_source (the_source_stream, sss, GETL_BATCH, ERRMODE_CONTINUE);
309
310   for (;;)
311     {
312       enum cmd_result result = cmd_parse (lexer, the_dataset);
313
314       if ( cmd_result_is_failure (result))
315         {
316           retval = FALSE;
317           if ( source_stream_current_error_mode (the_source_stream)
318                == ERRMODE_STOP )
319             break;
320         }
321
322       if ( result == CMD_EOF || result == CMD_FINISH)
323         break;
324     }
325
326   getl_abort_noninteractive (the_source_stream);
327
328   lex_destroy (lexer);
329
330   psppire_dict_replace_dictionary (the_data_store->dict,
331                                    dataset_dict (the_dataset));
332
333   reader = proc_extract_active_file_data (the_dataset);
334   if (!lazy_casereader_destroy (reader, lazy_serial))
335     psppire_data_store_set_reader (the_data_store, reader);
336
337   som_flush ();
338
339   psppire_output_window_reload ();
340
341   return retval;
342 }
343
344
345
346 /* Create a deep copy of SRC */
347 GtkListStore *
348 clone_list_store (const GtkListStore *src)
349 {
350   GtkTreeIter src_iter;
351   gboolean ok;
352   gint i;
353   const gint n_cols =  gtk_tree_model_get_n_columns (GTK_TREE_MODEL (src));
354   GType *types = g_malloc (sizeof (*types) *  n_cols);
355
356   int row = 0;
357   GtkListStore *dest;
358
359   for (i = 0 ; i < n_cols; ++i )
360     types[i] = gtk_tree_model_get_column_type (GTK_TREE_MODEL (src), i);
361
362   dest = gtk_list_store_newv (n_cols, types);
363
364   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (src),
365                                            &src_iter);
366        ok;
367        ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (src), &src_iter))
368     {
369       GtkTreeIter dest_iter;
370       gtk_list_store_append  (dest, &dest_iter);
371
372       for (i = 0 ; i < n_cols; ++i )
373         {
374           GValue val = {0};
375
376           gtk_tree_model_get_value (GTK_TREE_MODEL (src), &src_iter, i, &val);
377           gtk_list_store_set_value (dest, &dest_iter, i, &val);
378
379           g_value_unset (&val);
380         }
381       row++;
382     }
383
384   g_free (types);
385
386   return dest;
387 }
388
389
390 void
391 paste_syntax_in_new_window (const gchar *syntax)
392 {
393   GtkWidget *se = psppire_syntax_window_new ();
394
395   gtk_text_buffer_insert_at_cursor (PSPPIRE_SYNTAX_WINDOW (se)->buffer, syntax, -1);
396
397   gtk_widget_show (se);
398 }