Allow non-ascii characters to be entered into the variable and data sheets.
[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
170 #define _(msgid) gettext (msgid)
171 #define N_(msgid) msgid
172
173
174 static void
175 give_help (void)
176 {
177   GtkWidget *dialog;
178
179   dialog = gtk_message_dialog_new (NULL,
180                                    GTK_DIALOG_MODAL,
181                                    GTK_MESSAGE_INFO,
182                                    GTK_BUTTONS_CLOSE,
183                                    _("Sorry. The help system hasn't yet "
184                                      "been implemented."));
185   gtk_dialog_run (GTK_DIALOG (dialog));
186   gtk_widget_destroy (dialog);
187 }
188
189 void
190 connect_help (GtkBuilder *xml)
191 {
192   GSList *helps = gtk_builder_get_objects (xml);
193
194   GSList *i;
195   for ( i = helps; i ; i = g_slist_next (i))
196     {
197       GObject *o = i->data;
198       if ( GTK_IS_WIDGET (o) )
199         {
200           gchar *name = NULL;
201           gchar s[12] = {0};
202           g_object_get (o, "name", &name, NULL);
203
204           if ( name)
205             strncpy (s, name, 11);
206           s[11] = '\0';
207
208
209           if ( 0 == strcmp ("help_button", s))
210             {
211             g_signal_connect (o, "clicked", give_help, 0);
212             }
213         }
214     }
215
216   g_slist_free (helps);
217 }
218
219
220 void
221 reference_manual (GtkMenuItem *menu, gpointer data)
222 {
223   GError *err = NULL;
224   gchar *cmd = g_strdup_printf ("yelp file://%s", relocate (DOCDIR "/pspp.xml"));
225
226   if ( ! g_spawn_command_line_async (cmd, &err) )
227     {
228       msg (ME, _("Cannot open reference manual: %s"), err->message);
229     }
230
231   g_free (cmd);
232   g_clear_error (&err);
233 }
234
235
236 extern struct dataset *the_dataset;
237 extern struct source_stream *the_source_stream;
238 extern PsppireDataStore *the_data_store;
239
240 /* Lazy casereader callback function used by execute_syntax. */
241 static struct casereader *
242 create_casereader_from_data_store (void *data_store_)
243 {
244   PsppireDataStore *data_store = data_store_;
245   return psppire_data_store_get_reader (data_store);
246 }
247
248 gboolean
249 execute_syntax (struct getl_interface *sss)
250 {
251   struct lexer *lexer;
252   gboolean retval = TRUE;
253
254   struct casereader *reader;
255   size_t value_cnt;
256   casenumber case_cnt;
257   unsigned long int lazy_serial;
258
259   /* When the user executes a number of snippets of syntax in a
260      row, none of which read from the active file, the GUI becomes
261      progressively less responsive.  The reason is that each syntax
262      execution encapsulates the active file data in another
263      datasheet layer.  The cumulative effect of having a number of
264      layers of datasheets wastes time and space.
265
266      To solve the problem, we use a "lazy casereader", a wrapper
267      around the casereader obtained from the data store, that
268      only actually instantiates that casereader when it is
269      needed.  If the data store casereader is never needed, then
270      it is reused the next time syntax is run, without wrapping
271      it in another layer. */
272   value_cnt = psppire_data_store_get_value_count (the_data_store);
273   case_cnt = psppire_data_store_get_case_count (the_data_store);
274   reader = lazy_casereader_create (value_cnt, case_cnt,
275                                    create_casereader_from_data_store,
276                                    the_data_store, &lazy_serial);
277   proc_set_active_file_data (the_dataset, reader);
278
279   g_return_val_if_fail (proc_has_active_file (the_dataset), FALSE);
280
281   lexer = lex_create (the_source_stream);
282
283   getl_append_source (the_source_stream, sss, GETL_BATCH, ERRMODE_CONTINUE);
284
285   for (;;)
286     {
287       enum cmd_result result = cmd_parse (lexer, the_dataset);
288
289       if ( cmd_result_is_failure (result))
290         {
291           retval = FALSE;
292           if ( source_stream_current_error_mode (the_source_stream)
293                == ERRMODE_STOP )
294             break;
295         }
296
297       if ( result == CMD_EOF || result == CMD_FINISH)
298         break;
299     }
300
301   getl_abort_noninteractive (the_source_stream);
302
303   lex_destroy (lexer);
304
305   psppire_dict_replace_dictionary (the_data_store->dict,
306                                    dataset_dict (the_dataset));
307
308   reader = proc_extract_active_file_data (the_dataset);
309   if (!lazy_casereader_destroy (reader, lazy_serial))
310     psppire_data_store_set_reader (the_data_store, reader);
311
312   som_flush ();
313
314   psppire_output_window_reload ();
315
316   return retval;
317 }
318
319
320
321 /* Create a deep copy of SRC */
322 GtkListStore *
323 clone_list_store (const GtkListStore *src)
324 {
325   GtkTreeIter src_iter;
326   gboolean ok;
327   gint i;
328   const gint n_cols =  gtk_tree_model_get_n_columns (GTK_TREE_MODEL (src));
329   GType *types = g_malloc (sizeof (*types) *  n_cols);
330
331   int row = 0;
332   GtkListStore *dest;
333
334   for (i = 0 ; i < n_cols; ++i )
335     types[i] = gtk_tree_model_get_column_type (GTK_TREE_MODEL (src), i);
336
337   dest = gtk_list_store_newv (n_cols, types);
338
339   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (src),
340                                            &src_iter);
341        ok;
342        ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (src), &src_iter))
343     {
344       GtkTreeIter dest_iter;
345       gtk_list_store_append  (dest, &dest_iter);
346
347       for (i = 0 ; i < n_cols; ++i )
348         {
349           GValue val = {0};
350
351           gtk_tree_model_get_value (GTK_TREE_MODEL (src), &src_iter, i, &val);
352           gtk_list_store_set_value (dest, &dest_iter, i, &val);
353
354           g_value_unset (&val);
355         }
356       row++;
357     }
358
359   g_free (types);
360
361   return dest;
362 }
363
364
365 void
366 paste_syntax_in_new_window (const gchar *syntax)
367 {
368   GtkWidget *se = psppire_syntax_window_new ();
369
370   gtk_text_buffer_insert_at_cursor (PSPPIRE_SYNTAX_WINDOW (se)->buffer, syntax, -1);
371
372   gtk_widget_show (se);
373 }