Merge commit 'origin/stable'
[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        <glib-object.h>
24
25 #include <glib.h>
26 #include "helper.h"
27 #include "message-dialog.h"
28 #include <data/format.h>
29 #include <data/data-in.h>
30 #include <data/data-out.h>
31 #include <data/dictionary.h>
32 #include <data/casereader-provider.h>
33 #include <libpspp/message.h>
34
35 #include <gtk/gtkbuilder.h>
36 #include <libpspp/i18n.h>
37
38 #include <ctype.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <data/settings.h>
42
43 #include <language/command.h>
44 #include <data/lazy-casereader.h>
45 #include <data/procedure.h>
46 #include <language/lexer/lexer.h>
47 #include "psppire-data-store.h"
48 #include <output/manager.h>
49 #include "output-viewer.h"
50
51 #include "xalloc.h"
52
53 #include <gettext.h>
54
55 /* Formats a value according to FORMAT
56    The returned string must be freed when no longer required */
57 gchar *
58 value_to_text (union value v, struct fmt_spec format)
59 {
60   gchar *s = 0;
61
62   s = g_new (gchar, format.w + 1);
63   data_out (&v, &format, s);
64   s[format.w]='\0';
65   g_strchug (s);
66
67   return s;
68 }
69
70
71
72 gboolean
73 text_to_value (const gchar *text, union value *v,
74               struct fmt_spec format)
75 {
76   bool ok;
77
78   if ( format.type != FMT_A)
79     {
80       if ( ! text ) return FALSE;
81
82       {
83         const gchar *s = text;
84         while (*s)
85           {
86             if ( !isspace (*s))
87               break;
88             s++;
89           }
90
91         if ( !*s) return FALSE;
92       }
93     }
94
95   msg_disable ();
96   ok = data_in (ss_cstr (text), LEGACY_NATIVE, format.type, 0, 0, 0,
97                 v, fmt_var_width (&format));
98   msg_enable ();
99
100   return ok;
101 }
102
103
104 GtkBuilder *
105 builder_new_real (const gchar *name)
106 {
107   GtkBuilder *builder = gtk_builder_new ();
108
109   GError *err = NULL;
110   if ( ! gtk_builder_add_from_file (builder, name,  &err))
111     {
112       g_critical ("Couldnt open user interface  file %s: %s", name, err->message);
113       g_clear_error (&err);
114     }
115
116   return builder;
117 }
118
119
120
121 GtkWidget *
122 get_widget_assert (gpointer x, const gchar *name)
123 {
124   GObject *obj = G_OBJECT (x);
125   GtkWidget *w = NULL;
126   g_assert (name);
127
128   if (GTK_IS_BUILDER (obj))
129     w = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (obj), name));
130
131   if (GLADE_IS_XML (obj))
132     w = glade_xml_get_widget (GLADE_XML (obj), name);
133
134   if ( !w )
135     g_critical ("Widget \"%s\" could not be found\n", name);
136
137   return w;
138 }
139
140 /* Converts a string in the pspp locale to utf-8.
141    The return value must be freed when no longer required*/
142 gchar *
143 pspp_locale_to_utf8 (const gchar *text, gssize len, GError **err)
144 {
145   return recode_string (CONV_PSPP_TO_UTF8, text, len);
146 }
147
148 #define _(msgid) gettext (msgid)
149 #define N_(msgid) msgid
150
151
152 static void
153 give_help (void)
154 {
155   GtkWidget *dialog;
156
157   dialog = gtk_message_dialog_new (NULL,
158                                    GTK_DIALOG_MODAL,
159                                    GTK_MESSAGE_INFO,
160                                    GTK_BUTTONS_CLOSE,
161                                    _("Sorry. The help system hasn't yet "
162                                      "been implemented."));
163   gtk_dialog_run (GTK_DIALOG (dialog));
164   gtk_widget_destroy (dialog);
165 }
166
167 void
168 connect_help (GladeXML *xml)
169 {
170   GList *helps = glade_xml_get_widget_prefix (xml, "help_button_");
171
172   GList *i;
173   for ( i = g_list_first (helps); i ; i = g_list_next (i))
174     g_signal_connect (GTK_WIDGET (i->data), "clicked", give_help, 0);
175
176   g_list_free (helps);
177 }
178
179
180
181 void
182 reference_manual (GtkMenuItem *menu, gpointer data)
183 {
184   GError *err = NULL;
185   if ( ! g_spawn_command_line_async ("yelp info:pspp", &err) )
186     {
187       msg (ME, _("Cannot open reference manual: %s"), err->message);
188     }
189   g_clear_error (&err);
190 }
191
192
193 extern struct dataset *the_dataset;
194 extern struct source_stream *the_source_stream;
195 extern PsppireDataStore *the_data_store;
196
197 /* Lazy casereader callback function used by execute_syntax. */
198 static struct casereader *
199 create_casereader_from_data_store (void *data_store_)
200 {
201   PsppireDataStore *data_store = data_store_;
202   return psppire_data_store_get_reader (data_store);
203 }
204
205 gboolean
206 execute_syntax (struct getl_interface *sss)
207 {
208   struct lexer *lexer;
209   gboolean retval = TRUE;
210
211   struct casereader *reader;
212   size_t value_cnt;
213   casenumber case_cnt;
214   unsigned long int lazy_serial;
215
216   /* When the user executes a number of snippets of syntax in a
217      row, none of which read from the active file, the GUI becomes
218      progressively less responsive.  The reason is that each syntax
219      execution encapsulates the active file data in another
220      datasheet layer.  The cumulative effect of having a number of
221      layers of datasheets wastes time and space.
222
223      To solve the problem, we use a "lazy casereader", a wrapper
224      around the casereader obtained from the data store, that
225      only actually instantiates that casereader when it is
226      needed.  If the data store casereader is never needed, then
227      it is reused the next time syntax is run, without wrapping
228      it in another layer. */
229   value_cnt = psppire_data_store_get_value_count (the_data_store);
230   case_cnt = psppire_data_store_get_case_count (the_data_store);
231   reader = lazy_casereader_create (value_cnt, case_cnt,
232                                    create_casereader_from_data_store,
233                                    the_data_store, &lazy_serial);
234   proc_set_active_file_data (the_dataset, reader);
235
236   g_return_val_if_fail (proc_has_active_file (the_dataset), FALSE);
237
238   lexer = lex_create (the_source_stream);
239
240   getl_append_source (the_source_stream, sss, GETL_BATCH, ERRMODE_CONTINUE);
241
242   for (;;)
243     {
244       enum cmd_result result = cmd_parse (lexer, the_dataset);
245
246       if ( cmd_result_is_failure (result))
247         {
248           retval = FALSE;
249           if ( source_stream_current_error_mode (the_source_stream)
250                == ERRMODE_STOP )
251             break;
252         }
253
254       if ( result == CMD_EOF || result == CMD_FINISH)
255         break;
256     }
257
258   getl_abort_noninteractive (the_source_stream);
259
260   lex_destroy (lexer);
261
262   psppire_dict_replace_dictionary (the_data_store->dict,
263                                    dataset_dict (the_dataset));
264
265   reader = proc_extract_active_file_data (the_dataset);
266   if (!lazy_casereader_destroy (reader, lazy_serial))
267     psppire_data_store_set_reader (the_data_store, reader);
268
269   som_flush ();
270
271   reload_the_viewer ();
272
273   return retval;
274 }
275
276
277
278 /* Create a deep copy of SRC */
279 GtkListStore *
280 clone_list_store (const GtkListStore *src)
281 {
282   GtkTreeIter src_iter;
283   gboolean ok;
284   gint i;
285   const gint n_cols =  gtk_tree_model_get_n_columns (GTK_TREE_MODEL (src));
286   GType *types = g_malloc (sizeof (*types) *  n_cols);
287
288   int row = 0;
289   GtkListStore *dest;
290
291   for (i = 0 ; i < n_cols; ++i )
292     types[i] = gtk_tree_model_get_column_type (GTK_TREE_MODEL (src), i);
293
294   dest = gtk_list_store_newv (n_cols, types);
295
296   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (src),
297                                            &src_iter);
298        ok;
299        ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (src), &src_iter))
300     {
301       GtkTreeIter dest_iter;
302       gtk_list_store_append  (dest, &dest_iter);
303
304       for (i = 0 ; i < n_cols; ++i )
305         {
306           GValue val = {0};
307
308           gtk_tree_model_get_value (GTK_TREE_MODEL (src), &src_iter, i, &val);
309           gtk_list_store_set_value (dest, &dest_iter, i, &val);
310
311           g_value_unset (&val);
312         }
313       row++;
314     }
315
316   g_free (types);
317
318   return dest;
319 }
320
321
322