1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2017 Free Software Foundation
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.
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.
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/>. */
19 #define _(msgid) gettext (msgid)
20 #define P_(msgid) msgid
22 #include "psppire-delimited-text.h"
23 #include "psppire-text-file.h"
24 #include "language/data-io/data-parser.h"
25 #include "libpspp/str.h"
26 #include "libpspp/string-array.h"
27 #include "libpspp/i18n.h"
41 static struct data_parser *
42 make_data_parser (PsppireDelimitedText *tf)
44 struct data_parser *parser = data_parser_create ();
45 data_parser_set_type (parser, DP_DELIMITED);
46 data_parser_set_span (parser, false);
47 data_parser_set_quotes (parser, ss_empty ());
48 data_parser_set_quote_escape (parser, true);
49 data_parser_set_empty_line_has_field (parser, true);
52 struct string hard_delimiters = DS_EMPTY_INITIALIZER;
54 for (del = tf->delimiters; del; del = g_slist_next (del))
56 gunichar c = GPOINTER_TO_INT (del->data);
60 ds_put_unichar (&hard_delimiters, c);
62 data_parser_set_soft_delimiters (parser, ss_cstr (space ? " " : ""));
63 data_parser_set_hard_delimiters (parser, ds_ss (&hard_delimiters));
64 ds_destroy (&hard_delimiters);
68 struct string quote = DS_EMPTY_INITIALIZER;
69 ds_put_unichar ("e, tf->quote);
70 data_parser_set_quotes (parser, ds_ss ("e));
77 count_delims (PsppireDelimitedText *tf)
79 if (tf->child == NULL)
82 struct data_parser *parser = make_data_parser (tf);
87 for (valid = gtk_tree_model_get_iter_first (tf->child, &iter);
89 valid = gtk_tree_model_iter_next (tf->child, &iter))
92 gtk_tree_model_get (tf->child, &iter, 1, &line, -1);
93 size_t n_fields = data_parser_split (parser, ss_cstr (line), NULL);
94 if (n_fields > tf->max_fields)
95 tf->max_fields = n_fields;
99 data_parser_destroy (parser);
103 cache_invalidate (PsppireDelimitedText *tf)
106 data_parser_destroy (tf->parser);
107 tf->parser = make_data_parser (tf);
111 psppire_delimited_text_set_property (GObject *object,
116 PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (object);
120 case PROP_FIRST_LINE:
121 tf->first_line = g_value_get_int (value);
124 tf->child = g_value_get_object (value);
125 g_return_if_fail (PSPPIRE_IS_TEXT_FILE (tf->child));
127 case PROP_DELIMITERS:
128 g_slist_free (tf->delimiters);
129 tf->delimiters = g_slist_copy (g_value_get_pointer (value));
132 tf->quote = g_value_get_uint (value);
135 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
139 cache_invalidate (tf);
144 psppire_delimited_text_get_property (GObject *object,
149 PsppireDelimitedText *text_file = PSPPIRE_DELIMITED_TEXT (object);
153 case PROP_FIRST_LINE:
154 g_value_set_int (value, text_file->first_line);
156 case PROP_DELIMITERS:
157 g_value_set_pointer (value, text_file->delimiters);
160 g_value_set_uint (value, text_file->quote);
163 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
168 static void psppire_delimited_text_finalize (GObject *object);
169 static void psppire_delimited_text_dispose (GObject *object);
171 static GObjectClass *parent_class = NULL;
174 n_lines (PsppireDelimitedText *file)
176 PsppireTextFile *child = PSPPIRE_TEXT_FILE (file->child);
178 return child->maximum_lines;
182 __tree_get_iter (GtkTreeModel *tree_model,
186 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
191 gint *indices = gtk_tree_path_get_indices (path);
198 gint children = n_lines (file);
200 if (n >= children - file->first_line)
204 iter->user_data = GINT_TO_POINTER (n);
205 iter->stamp = file->stamp;
212 __tree_iter_next (GtkTreeModel *tree_model,
215 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
216 g_return_val_if_fail (file->stamp == iter->stamp, FALSE);
218 gint n = GPOINTER_TO_INT (iter->user_data);
221 gint children = n_lines (file);
223 if (n + 1 >= children - file->first_line)
226 iter->user_data = GINT_TO_POINTER (n + 1);
233 __tree_get_column_type (GtkTreeModel *tree_model,
239 return G_TYPE_STRING;
243 __iter_has_child (GtkTreeModel *tree_model,
251 __iter_parent (GtkTreeModel *tree_model,
259 __tree_get_path (GtkTreeModel *tree_model,
262 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
263 g_return_val_if_fail (file->stamp == iter->stamp, FALSE);
265 gint n = GPOINTER_TO_INT (iter->user_data);
267 gint children = n_lines (file);
269 if (n >= children - file->first_line)
272 return gtk_tree_path_new_from_indices (n, -1);
277 __iter_children (GtkTreeModel *tree_model,
286 __tree_model_iter_n_children (GtkTreeModel *tree_model,
289 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
290 g_assert (iter == NULL);
292 gint children = n_lines (file);
294 return children - file->first_line;
297 static GtkTreeModelFlags
298 __tree_model_get_flags (GtkTreeModel *model)
300 g_return_val_if_fail (PSPPIRE_IS_DELIMITED_TEXT (model), (GtkTreeModelFlags) 0);
302 return GTK_TREE_MODEL_LIST_ONLY;
306 __tree_model_get_n_columns (GtkTreeModel *tree_model)
308 PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (tree_model);
310 /* +1 for the leading line number column */
311 return tf->max_fields + 1;
316 __iter_nth_child (GtkTreeModel *tree_model,
321 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
323 g_assert (parent == NULL);
325 g_return_val_if_fail (file, FALSE);
327 gint children = gtk_tree_model_iter_n_children (file->child, NULL);
329 if (n >= children - file->first_line)
332 iter->user_data = NULL;
336 iter->user_data = GINT_TO_POINTER (n);
337 iter->stamp = file->stamp;
342 /* Split row N into it's delimited fields (if it is not already cached)
343 and set this row as the current cache. */
345 split_row_into_fields (PsppireDelimitedText *file, gint n)
347 if (n == file->cache_row) /* Cache hit */
350 file->parser = make_data_parser (file);
352 string_array_clear (&file->cache);
353 data_parser_split (file->parser, PSPPIRE_TEXT_FILE (file->child)->lines[n],
359 psppire_delimited_text_get_header_title (PsppireDelimitedText *file, gint column)
361 if (file->first_line <= 0)
364 split_row_into_fields (file, file->first_line - 1);
366 return column < file->cache.n ? file->cache.strings[column] : "";
370 __get_value (GtkTreeModel *tree_model,
375 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
377 g_return_if_fail (iter->stamp == file->stamp);
379 gint n = GPOINTER_TO_INT (iter->user_data) + file->first_line;
384 g_value_init (value, G_TYPE_INT);
385 g_value_set_int (value, n + 1);
389 g_value_init (value, G_TYPE_STRING);
391 split_row_into_fields (file, n);
393 size_t idx = column - 1;
394 const char *s = idx < file->cache.n ? file->cache.strings[idx] : "";
395 g_value_set_string (value, s);
400 __tree_model_init (GtkTreeModelIface *iface)
402 iface->get_flags = __tree_model_get_flags;
403 iface->get_n_columns = __tree_model_get_n_columns ;
404 iface->get_column_type = __tree_get_column_type;
405 iface->get_iter = __tree_get_iter;
406 iface->iter_next = __tree_iter_next;
407 iface->get_path = __tree_get_path;
408 iface->get_value = __get_value;
410 iface->iter_children = __iter_children;
411 iface->iter_has_child = __iter_has_child;
412 iface->iter_n_children = __tree_model_iter_n_children;
413 iface->iter_nth_child = __iter_nth_child;
414 iface->iter_parent = __iter_parent;
417 G_DEFINE_TYPE_WITH_CODE (PsppireDelimitedText, psppire_delimited_text, G_TYPE_OBJECT,
418 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
422 psppire_delimited_text_class_init (PsppireDelimitedTextClass *class)
424 GObjectClass *object_class;
426 parent_class = g_type_class_peek_parent (class);
427 object_class = G_OBJECT_CLASS (class);
429 GParamSpec *first_line_spec =
430 g_param_spec_int ("first-line",
432 P_("The first line to be considered."),
436 GParamSpec *delimiters_spec =
437 g_param_spec_pointer ("delimiters",
439 P_("A GSList of gunichars which delimit the fields."),
442 GParamSpec *quote_spec =
443 g_param_spec_unichar ("quote",
445 P_("A character that quotes the field, or 0 to disable quoting."),
449 GParamSpec *child_spec =
450 g_param_spec_object ("child",
452 P_("The GtkTextModel which this object wraps."),
454 G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
456 object_class->set_property = psppire_delimited_text_set_property;
457 object_class->get_property = psppire_delimited_text_get_property;
459 g_object_class_install_property (object_class,
463 g_object_class_install_property (object_class,
467 g_object_class_install_property (object_class,
471 g_object_class_install_property (object_class,
475 object_class->finalize = psppire_delimited_text_finalize;
476 object_class->dispose = psppire_delimited_text_dispose;
481 psppire_delimited_text_init (PsppireDelimitedText *text_file)
483 text_file->child = NULL;
484 text_file->first_line = 0;
485 text_file->delimiters = g_slist_prepend (NULL, GINT_TO_POINTER (':'));
487 text_file->cache_row = -1;
488 string_array_init (&text_file->cache);
489 text_file->parser = NULL;
491 text_file->max_fields = 0;
493 text_file->quote = 0;
495 text_file->dispose_has_run = FALSE;
496 text_file->stamp = g_random_int ();
500 PsppireDelimitedText *
501 psppire_delimited_text_new (GtkTreeModel *child)
504 g_object_new (PSPPIRE_TYPE_DELIMITED_TEXT,
510 psppire_delimited_text_finalize (GObject *object)
512 PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (object);
514 g_slist_free (tf->delimiters);
515 string_array_destroy (&tf->cache);
516 data_parser_destroy (tf->parser);
519 (* parent_class->finalize) (object);
524 psppire_delimited_text_dispose (GObject *object)
526 PsppireDelimitedText *ds = PSPPIRE_DELIMITED_TEXT (object);
528 if (ds->dispose_has_run)
532 (* parent_class->dispose) (object);
534 ds->dispose_has_run = TRUE;