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