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