Merge commit 'HEAD'; commit 'savannah/master'
[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
123 GtkWidget *
124 get_widget_assert (gpointer x, const gchar *name)
125 {
126   GObject *obj = G_OBJECT (x);
127   GtkWidget *w = NULL;
128   g_assert (name);
129
130   if (GTK_IS_BUILDER (obj))
131     w = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (obj), name));
132
133   if (GLADE_IS_XML (obj))
134     w = glade_xml_get_widget (GLADE_XML (obj), name);
135
136   if ( !w )
137     g_critical ("Widget \"%s\" could not be found\n", name);
138
139   return w;
140 }
141
142 /* Converts a string in the pspp locale to utf-8.
143    The return value must be freed when no longer required*/
144 gchar *
145 pspp_locale_to_utf8 (const gchar *text, gssize len, GError **err)
146 {
147   return recode_string (CONV_PSPP_TO_UTF8, text, len);
148 }
149
150 #define _(msgid) gettext (msgid)
151 #define N_(msgid) msgid
152
153
154 static void
155 give_help (void)
156 {
157   GtkWidget *dialog;
158
159   dialog = gtk_message_dialog_new (NULL,
160                                    GTK_DIALOG_MODAL,
161                                    GTK_MESSAGE_INFO,
162                                    GTK_BUTTONS_CLOSE,
163                                    _("Sorry. The help system hasn't yet "
164                                      "been implemented."));
165   gtk_dialog_run (GTK_DIALOG (dialog));
166   gtk_widget_destroy (dialog);
167 }
168
169 void
170 connect_help (GladeXML *xml)
171 {
172   GList *helps = glade_xml_get_widget_prefix (xml, "help_button_");
173
174   GList *i;
175   for ( i = g_list_first (helps); i ; i = g_list_next (i))
176     g_signal_connect (GTK_WIDGET (i->data), "clicked", give_help, 0);
177
178   g_list_free (helps);
179 }
180
181
182
183 void
184 reference_manual (GtkMenuItem *menu, gpointer data)
185 {
186   GError *err = NULL;
187   if ( ! g_spawn_command_line_async ("yelp info:pspp", &err) )
188     {
189       msg (ME, _("Cannot open reference manual: %s"), err->message);
190     }
191   g_clear_error (&err);
192 }
193
194
195 extern struct dataset *the_dataset;
196 extern struct source_stream *the_source_stream;
197 extern PsppireDataStore *the_data_store;
198
199 /* Lazy casereader callback function used by execute_syntax. */
200 static struct casereader *
201 create_casereader_from_data_store (void *data_store_)
202 {
203   PsppireDataStore *data_store = data_store_;
204   return psppire_data_store_get_reader (data_store);
205 }
206
207 gboolean
208 execute_syntax (struct getl_interface *sss)
209 {
210   struct lexer *lexer;
211   gboolean retval = TRUE;
212
213   struct casereader *reader;
214   size_t value_cnt;
215   casenumber case_cnt;
216   unsigned long int lazy_serial;
217
218   /* When the user executes a number of snippets of syntax in a
219      row, none of which read from the active file, the GUI becomes
220      progressively less responsive.  The reason is that each syntax
221      execution encapsulates the active file data in another
222      datasheet layer.  The cumulative effect of having a number of
223      layers of datasheets wastes time and space.
224
225      To solve the problem, we use a "lazy casereader", a wrapper
226      around the casereader obtained from the data store, that
227      only actually instantiates that casereader when it is
228      needed.  If the data store casereader is never needed, then
229      it is reused the next time syntax is run, without wrapping
230      it in another layer. */
231   value_cnt = psppire_data_store_get_value_count (the_data_store);
232   case_cnt = psppire_data_store_get_case_count (the_data_store);
233   reader = lazy_casereader_create (value_cnt, case_cnt,
234                                    create_casereader_from_data_store,
235                                    the_data_store, &lazy_serial);
236   proc_set_active_file_data (the_dataset, reader);
237
238   g_return_val_if_fail (proc_has_active_file (the_dataset), FALSE);
239
240   lexer = lex_create (the_source_stream);
241
242   getl_append_source (the_source_stream, sss, GETL_BATCH, ERRMODE_CONTINUE);
243
244   for (;;)
245     {
246       enum cmd_result result = cmd_parse (lexer, the_dataset);
247
248       if ( cmd_result_is_failure (result))
249         {
250           retval = FALSE;
251           if ( source_stream_current_error_mode (the_source_stream)
252                == ERRMODE_STOP )
253             break;
254         }
255
256       if ( result == CMD_EOF || result == CMD_FINISH)
257         break;
258     }
259
260   getl_abort_noninteractive (the_source_stream);
261
262   lex_destroy (lexer);
263
264   psppire_dict_replace_dictionary (the_data_store->dict,
265                                    dataset_dict (the_dataset));
266
267   reader = proc_extract_active_file_data (the_dataset);
268   if (!lazy_casereader_destroy (reader, lazy_serial))
269     psppire_data_store_set_reader (the_data_store, reader);
270
271   som_flush ();
272
273   psppire_output_window_reload ();
274
275   return retval;
276 }
277
278
279
280 /* Create a deep copy of SRC */
281 GtkListStore *
282 clone_list_store (const GtkListStore *src)
283 {
284   GtkTreeIter src_iter;
285   gboolean ok;
286   gint i;
287   const gint n_cols =  gtk_tree_model_get_n_columns (GTK_TREE_MODEL (src));
288   GType *types = g_malloc (sizeof (*types) *  n_cols);
289
290   int row = 0;
291   GtkListStore *dest;
292
293   for (i = 0 ; i < n_cols; ++i )
294     types[i] = gtk_tree_model_get_column_type (GTK_TREE_MODEL (src), i);
295
296   dest = gtk_list_store_newv (n_cols, types);
297
298   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (src),
299                                            &src_iter);
300        ok;
301        ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (src), &src_iter))
302     {
303       GtkTreeIter dest_iter;
304       gtk_list_store_append  (dest, &dest_iter);
305
306       for (i = 0 ; i < n_cols; ++i )
307         {
308           GValue val = {0};
309
310           gtk_tree_model_get_value (GTK_TREE_MODEL (src), &src_iter, i, &val);
311           gtk_list_store_set_value (dest, &dest_iter, i, &val);
312
313           g_value_unset (&val);
314         }
315       row++;
316     }
317
318   g_free (types);
319
320   return dest;
321 }
322
323
324
325 void
326 paste_syntax_in_new_window (const gchar *syntax)
327 {
328   GtkWidget *se = psppire_syntax_window_new ();
329
330   gtk_text_buffer_insert_at_cursor (PSPPIRE_SYNTAX_WINDOW (se)->buffer, syntax, -1);
331
332   gtk_widget_show (se);
333 }