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 "libpspp/str.h"
25 #include "libpspp/i18n.h"
44 static const struct enclosure enclosures[3] =
52 count_delims (PsppireDelimitedText *tf)
54 if (tf->child == NULL)
57 tf->max_delimiters = 0;
60 for (valid = gtk_tree_model_get_iter_first (tf->child, &iter);
62 valid = gtk_tree_model_iter_next (tf->child, &iter))
65 // FIXME: Box these lines to avoid constant allocation/deallocation
67 gtk_tree_model_get (tf->child, &iter, 1, &foo, -1);
73 const gunichar c = *line; //FIXME: Not multibyte safe!
77 for (i = 0; i < 3; ++i)
79 if (c == enclosures[i].opening)
86 else if (c == enclosures[enc].closing)
93 for (del = tf->delimiters; del; del = g_slist_next (del))
95 if (c == GPOINTER_TO_INT (del->data))
101 tf->max_delimiters = MAX (tf->max_delimiters, count);
108 cache_invalidate (PsppireDelimitedText *tf)
110 memset (tf->cache_starts, 0, 512);
111 if (tf->const_cache.string)
113 ss_dealloc (&tf->const_cache);
114 tf->const_cache.string = NULL;
120 psppire_delimited_text_set_property (GObject *object,
125 PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (object);
129 case PROP_FIRST_LINE:
130 tf->first_line = g_value_get_int (value);
133 tf->child = g_value_get_object (value);
134 g_return_if_fail (PSPPIRE_IS_TEXT_FILE (tf->child));
136 case PROP_DELIMITERS:
137 g_slist_free (tf->delimiters);
138 tf->delimiters = g_slist_copy (g_value_get_pointer (value));
141 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
145 cache_invalidate (tf);
150 psppire_delimited_text_get_property (GObject *object,
155 PsppireDelimitedText *text_file = PSPPIRE_DELIMITED_TEXT (object);
159 case PROP_FIRST_LINE:
160 g_value_set_int (value, text_file->first_line);
162 case PROP_DELIMITERS:
163 g_value_set_pointer (value, text_file->delimiters);
166 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
172 static void psppire_delimited_text_init (PsppireDelimitedText *text_file);
173 static void psppire_delimited_text_class_init (PsppireDelimitedTextClass *class);
175 static void psppire_delimited_text_finalize (GObject *object);
176 static void psppire_delimited_text_dispose (GObject *object);
178 static GObjectClass *parent_class = NULL;
181 n_lines (PsppireDelimitedText *file)
183 PsppireTextFile *child = PSPPIRE_TEXT_FILE (file->child);
185 return child->maximum_lines;
189 __tree_get_iter (GtkTreeModel *tree_model,
193 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
198 gint *indices = gtk_tree_path_get_indices (path);
205 gint children = n_lines (file);
207 if (n >= children - file->first_line)
211 iter->user_data = GINT_TO_POINTER (n);
212 iter->stamp = file->stamp;
219 __tree_iter_next (GtkTreeModel *tree_model,
222 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
223 g_return_val_if_fail (file->stamp == iter->stamp, FALSE);
225 gint n = GPOINTER_TO_INT (iter->user_data);
228 gint children = n_lines (file);
230 if (n + 1 >= children - file->first_line)
233 iter->user_data = GINT_TO_POINTER (n + 1);
240 __tree_get_column_type (GtkTreeModel *tree_model,
246 return G_TYPE_STRING;
250 __iter_has_child (GtkTreeModel *tree_model,
258 __iter_parent (GtkTreeModel *tree_model,
266 __tree_get_path (GtkTreeModel *tree_model,
269 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
270 g_return_val_if_fail (file->stamp == iter->stamp, FALSE);
272 gint n = GPOINTER_TO_INT (iter->user_data);
274 gint children = n_lines (file);
276 if (n >= children - file->first_line)
279 return gtk_tree_path_new_from_indices (n, -1);
284 __iter_children (GtkTreeModel *tree_model,
293 __tree_model_iter_n_children (GtkTreeModel *tree_model,
296 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
297 g_assert (iter == NULL);
299 gint children = n_lines (file);
301 return children - file->first_line;
304 static GtkTreeModelFlags
305 __tree_model_get_flags (GtkTreeModel *model)
307 g_return_val_if_fail (PSPPIRE_IS_DELIMITED_TEXT (model), (GtkTreeModelFlags) 0);
309 return GTK_TREE_MODEL_LIST_ONLY;
313 __tree_model_get_n_columns (GtkTreeModel *tree_model)
315 PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (tree_model);
317 /* + 1 for the trailing field and +1 for the leading line number column */
318 return tf->max_delimiters + 1 + 1;
323 __iter_nth_child (GtkTreeModel *tree_model,
328 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
330 g_assert (parent == NULL);
332 g_return_val_if_fail (file, FALSE);
334 gint children = gtk_tree_model_iter_n_children (file->child, NULL);
336 if (n >= children - file->first_line)
339 iter->user_data = NULL;
343 iter->user_data = GINT_TO_POINTER (n);
344 iter->stamp = file->stamp;
351 nullify_char (struct substring cs)
353 int char_len = ss_first_mblen (cs);
356 cs.string[char_len - 1] = '\0';
362 /* Split row N into it's delimited fields (if it is not already cached)
363 and set this row as the current cache. */
365 split_row_into_fields (PsppireDelimitedText *file, gint n)
367 if (n == file->cache_row) /* Cache hit */
372 memset (file->cache_starts, 0, 512);
374 if (file->const_cache.string)
376 ss_dealloc (&file->const_cache);
378 ss_alloc_substring_pool (&file->const_cache,
379 PSPPIRE_TEXT_FILE (file->child)->lines[n], NULL);
380 struct substring cs = file->const_cache;
382 file->cache_starts[0] = cs.string;
385 UINT32_MAX != ss_first_mb (cs);
388 ucs4_t character = ss_first_mb (cs);
389 gboolean char_is_quote = FALSE;
393 for (i = 0; i < 3; ++i)
395 if (character == enclosures[i].opening)
398 char_is_quote = TRUE;
399 file->cache_starts[field] += ss_first_mblen (cs);
404 else if (character == enclosures[enc].closing)
406 char_is_quote = TRUE;
411 if (enc == -1 && char_is_quote == FALSE)
414 for (del = file->delimiters; del; del = g_slist_next (del))
416 if (character == GPOINTER_TO_INT (del->data))
419 int char_len = ss_first_mblen (cs);
420 file->cache_starts[field] = cs.string + char_len;
432 psppire_delimited_text_get_header_title (PsppireDelimitedText *file, gint column)
434 if (file->first_line <= 0)
437 split_row_into_fields (file, file->first_line - 1);
439 return file->cache_starts [column];
443 __get_value (GtkTreeModel *tree_model,
448 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
450 g_return_if_fail (iter->stamp == file->stamp);
452 gint n = GPOINTER_TO_INT (iter->user_data) + file->first_line;
457 g_value_init (value, G_TYPE_INT);
458 g_value_set_int (value, n + 1);
462 g_value_init (value, G_TYPE_STRING);
464 split_row_into_fields (file, n);
466 g_value_set_string (value, file->cache_starts [column - 1]);
471 __tree_model_init (GtkTreeModelIface *iface)
473 iface->get_flags = __tree_model_get_flags;
474 iface->get_n_columns = __tree_model_get_n_columns ;
475 iface->get_column_type = __tree_get_column_type;
476 iface->get_iter = __tree_get_iter;
477 iface->iter_next = __tree_iter_next;
478 iface->get_path = __tree_get_path;
479 iface->get_value = __get_value;
481 iface->iter_children = __iter_children;
482 iface->iter_has_child = __iter_has_child;
483 iface->iter_n_children = __tree_model_iter_n_children;
484 iface->iter_nth_child = __iter_nth_child;
485 iface->iter_parent = __iter_parent;
490 psppire_delimited_text_get_type (void)
492 static GType text_file_type = 0;
496 static const GTypeInfo text_file_info =
498 sizeof (PsppireDelimitedTextClass),
499 NULL, /* base_init */
500 NULL, /* base_finalize */
501 (GClassInitFunc) psppire_delimited_text_class_init,
502 NULL, /* class_finalize */
503 NULL, /* class_data */
504 sizeof (PsppireDelimitedText),
506 (GInstanceInitFunc) psppire_delimited_text_init,
509 static const GInterfaceInfo tree_model_info = {
510 (GInterfaceInitFunc) __tree_model_init,
515 text_file_type = g_type_register_static (G_TYPE_OBJECT,
516 "PsppireDelimitedText",
519 g_type_add_interface_static (text_file_type, GTK_TYPE_TREE_MODEL,
523 return text_file_type;
528 psppire_delimited_text_class_init (PsppireDelimitedTextClass *class)
530 GObjectClass *object_class;
532 parent_class = g_type_class_peek_parent (class);
533 object_class = (GObjectClass*) class;
535 GParamSpec *first_line_spec =
536 g_param_spec_int ("first-line",
538 P_("The first line to be considered."),
542 GParamSpec *delimiters_spec =
543 g_param_spec_pointer ("delimiters",
545 P_("A GSList of gunichars which delimit the fields."),
548 GParamSpec *child_spec =
549 g_param_spec_object ("child",
551 P_("The GtkTextModel which this object wraps."),
553 G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
555 object_class->set_property = psppire_delimited_text_set_property;
556 object_class->get_property = psppire_delimited_text_get_property;
558 g_object_class_install_property (object_class,
562 g_object_class_install_property (object_class,
566 g_object_class_install_property (object_class,
570 object_class->finalize = psppire_delimited_text_finalize;
571 object_class->dispose = psppire_delimited_text_dispose;
576 psppire_delimited_text_init (PsppireDelimitedText *text_file)
578 text_file->child = NULL;
579 text_file->first_line = 0;
580 text_file->delimiters = g_slist_prepend (NULL, GINT_TO_POINTER (':'));
582 text_file->const_cache.string = NULL;
583 text_file->const_cache.length = 0;
584 text_file->cache_row = -1;
585 memset (text_file->cache_starts, 0, 512);
587 text_file->max_delimiters = 0;
589 text_file->dispose_has_run = FALSE;
590 text_file->stamp = g_random_int ();
595 psppire_delimited_text_new (GtkTreeModel *child)
597 PsppireDelimitedText *retval =
598 g_object_new (PSPPIRE_TYPE_DELIMITED_TEXT,
602 return GTK_TREE_MODEL (retval);
606 psppire_delimited_text_finalize (GObject *object)
608 PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (object);
610 g_slist_free (tf->delimiters);
612 ss_dealloc (&tf->const_cache);
615 (* parent_class->finalize) (object);
620 psppire_delimited_text_dispose (GObject *object)
622 PsppireDelimitedText *ds = PSPPIRE_DELIMITED_TEXT (object);
624 if (ds->dispose_has_run)
628 (* parent_class->dispose) (object);
630 ds->dispose_has_run = TRUE;