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