Revert "Fixed a use after free error when manipulating datasets."
[pspp] / src / ui / gui / helper.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2009, 2010, 2011, 2012, 2013  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 <data/format.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 #include "psppire-syntax-window.h"
34 #include <gtk/gtk.h>
35 #include <libpspp/i18n.h>
36
37 #include <ctype.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <data/settings.h>
41
42 #include "psppire-data-store.h"
43
44 #include "gl/configmake.h"
45 #include "xalloc.h"
46
47 #include <gettext.h>
48
49 /* Returns a copy of IN with each underscore doubled.  The caller should free
50    the returned string (with free()) when it is no longer needed. */
51 char *
52 escape_underscores (const char *in)
53 {
54   char *out = xmalloc (2 * strlen (in) + 1);
55   char *p;
56
57   p = out;
58   for (; *in != '\0'; in++)
59     {
60       if (*in == '_')
61         *p++ = '_';
62       *p++ = *in;
63     }
64   *p = '\0';
65
66   return out;
67 }
68
69 /* Formats a value according to VAR's print format and strips white space
70    appropriately for VAR's type.  That is, if VAR is numeric, strips leading
71    white space (because numbers are right-justified within their fields), and
72    if VAR is string, strips trailing white space (because spaces pad out string
73    values on the right).
74
75    Returns an allocated string.  The returned string must be freed when no
76    longer required. */
77 gchar *
78 value_to_text (union value v, const struct variable *var)
79 {
80   return value_to_text__ (v, var_get_print_format (var),
81                           var_get_encoding (var));
82 }
83
84 /* Formats a value with format FORMAT and strips white space appropriately for
85    FORMATs' type.  That is, if FORMAT is numeric, strips leading white space
86    (because numbers are right-justified within their fields), and if FORMAT is
87    string, strips trailing white space (because spaces pad out string values on
88    the right).
89
90    Returns an allocated string.  The returned string must be freed when no
91    longer required. */
92 gchar *
93 value_to_text__ (union value v,
94                  const struct fmt_spec *format, const char *encoding)
95 {
96   gchar *s;
97
98   s = data_out_stretchy (&v, encoding, format, NULL);
99   if (fmt_is_numeric (format->type))
100     g_strchug (s);
101   else
102     g_strchomp (s);
103
104   return s;
105 }
106
107 /* Converts TEXT to a value.
108
109    VAL will be initialised and filled by this function.
110    It is the caller's responsibility to destroy VAL when no longer needed.
111    VAR must be the variable with which VAL is associated.
112
113    On success, VAL is returned, NULL otherwise.
114 */
115 union value *
116 text_to_value (const gchar *text,
117                const struct variable *var,
118                union value *val)
119 {
120   return text_to_value__ (text, var_get_print_format (var),
121                           var_get_encoding (var), val);
122 }
123
124 /* Converts TEXT, which contains a value in the given FORMAT encoding in
125    ENCODING, into a value.
126
127    VAL will be initialised and filled by this function.
128    It is the caller's responsibility to destroy VAL when no longer needed.
129
130    On success, VAL is returned, NULL otherwise.
131 */
132 union value *
133 text_to_value__ (const gchar *text,
134                  const struct fmt_spec *format,
135                  const gchar *encoding,
136                  union value *val)
137 {
138   int width = fmt_var_width (format);
139
140   if (format->type != FMT_A)
141     {
142       if (! text) return NULL;
143
144       {
145         const gchar *s = text;
146         while (*s)
147           {
148             if (!isspace (*s))
149               break;
150             s++;
151           }
152
153         if (!*s) return NULL;
154       }
155     }
156
157   value_init (val, width);
158   char *err = data_in (ss_cstr (text), UTF8, format->type, val, width, encoding);
159
160   if (err)
161     {
162       value_destroy (val, width);
163       val = NULL;
164       free (err);
165     }
166
167   return val;
168 }
169
170
171 /* This function must be used whenever a filename generated by glib,
172    (eg, from gtk_file_chooser_get_filename) and passed to the C library,
173    (eg through a pspp syntax string).
174 */
175 gchar *
176 convert_glib_filename_to_system_filename (const gchar *fname, GError **err)
177 {
178   gchar *output_name;
179
180 #ifdef G_OS_WIN32
181   const gchar *target_encoding;
182   gchar *utf8_name = NULL;
183
184   g_get_charset (&target_encoding);
185
186   output_name = g_convert (fname, -1, target_encoding,
187                         "UTF-8", NULL, NULL, err);
188 #else
189   output_name = xstrdup (fname);
190 #endif
191
192   return output_name;
193 }
194
195
196
197 #define _(msgid) gettext (msgid)
198 #define N_(msgid) msgid
199
200
201 static void
202 give_help (void)
203 {
204   GtkWidget *dialog;
205
206   dialog = gtk_message_dialog_new (NULL,
207                                    GTK_DIALOG_MODAL,
208                                    GTK_MESSAGE_INFO,
209                                    GTK_BUTTONS_CLOSE,
210                                    _("Sorry. The help system hasn't yet "
211                                      "been implemented."));
212   gtk_dialog_run (GTK_DIALOG (dialog));
213   gtk_widget_destroy (dialog);
214 }
215
216 void
217 connect_help (GtkBuilder *xml)
218 {
219   GSList *helps = gtk_builder_get_objects (xml);
220
221   GSList *i;
222   for (i = helps; i ; i = g_slist_next (i))
223     {
224       GObject *o = i->data;
225       if (GTK_IS_WIDGET (o))
226         {
227           const gchar *name = gtk_buildable_get_name (GTK_BUILDABLE (o));
228           gchar s[12] = {0};
229
230           if (name)
231             strncpy (s, name, 11);
232           s[11] = '\0';
233
234
235           if (0 == strcmp ("help_button", s))
236             {
237             g_signal_connect (o, "clicked", give_help, 0);
238             }
239         }
240     }
241
242   g_slist_free (helps);
243 }
244
245
246 /* Create a deep copy of SRC */
247 GtkListStore *
248 clone_list_store (const GtkListStore *src)
249 {
250   GtkTreeIter src_iter;
251   gboolean ok;
252   gint i;
253   const gint n_cols =  gtk_tree_model_get_n_columns (GTK_TREE_MODEL (src));
254   GType *types = g_malloc (sizeof (*types) *  n_cols);
255
256   int row = 0;
257   GtkListStore *dest;
258
259   for (i = 0 ; i < n_cols; ++i)
260     types[i] = gtk_tree_model_get_column_type (GTK_TREE_MODEL (src), i);
261
262   dest = gtk_list_store_newv (n_cols, types);
263
264   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (src),
265                                            &src_iter);
266        ok;
267        ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (src), &src_iter))
268     {
269       GtkTreeIter dest_iter;
270       gtk_list_store_append  (dest, &dest_iter);
271
272       for (i = 0 ; i < n_cols; ++i)
273         {
274           GValue val = {0};
275
276           gtk_tree_model_get_value (GTK_TREE_MODEL (src), &src_iter, i, &val);
277           gtk_list_store_set_value (dest, &dest_iter, i, &val);
278
279           g_value_unset (&val);
280         }
281       row++;
282     }
283
284   g_free (types);
285
286   return dest;
287 }
288
289
290
291
292 static gboolean
293 on_delete (GtkWindow *window, GdkEvent *e, GtkWindow **addr)
294 {
295   *addr = NULL;
296
297   return FALSE;
298 }
299
300 char *
301 paste_syntax_to_window (gchar *syntax)
302 {
303   static GtkWidget *the_syntax_pasteboard = NULL;
304
305   GtkTextBuffer *buffer = NULL;
306
307   if (NULL == the_syntax_pasteboard)
308     {
309       the_syntax_pasteboard = psppire_syntax_window_new (NULL);
310       g_signal_connect (the_syntax_pasteboard, "delete-event", G_CALLBACK (on_delete),
311                         &the_syntax_pasteboard);
312     }
313
314   buffer = GTK_TEXT_BUFFER (PSPPIRE_SYNTAX_WINDOW (the_syntax_pasteboard)->buffer);
315
316   gtk_text_buffer_begin_user_action (buffer);
317   gtk_text_buffer_insert_at_cursor (buffer, syntax, -1);
318   gtk_text_buffer_insert_at_cursor (buffer, "\n", 1);
319   gtk_text_buffer_end_user_action (buffer);
320
321   gtk_widget_show (the_syntax_pasteboard);
322
323   return syntax;
324 }
325
326
327 /* gtk_box_pack_start_defaults is deprecated.
328    Therefore we roll our own until a better solution is found */
329 void
330 psppire_box_pack_start_defaults (GtkBox *box, GtkWidget *widget)
331 {
332   gtk_box_pack_start (box, widget, TRUE, TRUE, 0);
333 }