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"
39 count_delims (PsppireDelimitedText *tf)
43 tf->max_delimiters = 0;
46 for (valid = gtk_tree_model_get_iter_first (tf->child, &iter);
48 valid = gtk_tree_model_iter_next (tf->child, &iter))
50 // FIXME: Box these lines to avoid constant allocation/deallocation
52 gtk_tree_model_get (tf->child, &iter, 1, &foo, -1);
59 for (del = tf->delimiters; del; del = g_slist_next (del))
61 if (*line == GPOINTER_TO_INT (del->data))
66 tf->max_delimiters = MAX (tf->max_delimiters, count);
71 // g_print ("Max Number of delimiters per row: %d\n", tf->max_delimiters);
75 cache_invalidate (PsppireDelimitedText *tf)
77 memset (tf->cache_starts, 0, 512);
78 if (tf->const_cache.string)
80 ss_dealloc (&tf->const_cache);
81 tf->const_cache.string = NULL;
87 psppire_delimited_text_set_property (GObject *object,
92 PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (object);
97 tf->first_line = g_value_get_int (value);
100 tf->child = g_value_get_object (value);
102 case PROP_DELIMITERS:
103 g_slist_free (tf->delimiters);
104 tf->delimiters = g_slist_copy (g_value_get_pointer (value));
107 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111 cache_invalidate (tf);
116 psppire_delimited_text_get_property (GObject *object,
121 PsppireDelimitedText *text_file = PSPPIRE_DELIMITED_TEXT (object);
125 case PROP_FIRST_LINE:
126 g_value_set_int (value, text_file->first_line);
128 case PROP_DELIMITERS:
129 g_value_set_pointer (value, text_file->delimiters);
132 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
138 static void psppire_delimited_text_init (PsppireDelimitedText *text_file);
139 static void psppire_delimited_text_class_init (PsppireDelimitedTextClass *class);
141 static void psppire_delimited_text_finalize (GObject *object);
142 static void psppire_delimited_text_dispose (GObject *object);
144 static GObjectClass *parent_class = NULL;
148 __tree_get_iter (GtkTreeModel *tree_model,
152 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
156 // g_print ("%s:%d %s %s\n", __FILE__, __LINE__, __FUNCTION__, gtk_tree_path_to_string (path));
158 gint *indices = gtk_tree_path_get_indices (path);
165 gint children = gtk_tree_model_iter_n_children (file->child, NULL);
167 if (n >= children - file->first_line)
170 // g_print ("%s:%d %s %d Children: %d\n", __FILE__, __LINE__, __FUNCTION__, n, children);
172 iter->user_data = GINT_TO_POINTER (n);
173 iter->stamp = file->stamp;
180 __tree_iter_next (GtkTreeModel *tree_model,
183 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
184 g_return_val_if_fail (file->stamp == iter->stamp, FALSE);
186 gint n = GPOINTER_TO_INT (iter->user_data);
188 // g_print ("%s:%d %s %d\n", __FILE__, __LINE__, __FUNCTION__, n);
190 gint children = gtk_tree_model_iter_n_children (file->child, NULL);
192 if (n + 1 >= children - file->first_line)
195 iter->user_data = GINT_TO_POINTER (n + 1);
202 __tree_get_column_type (GtkTreeModel *tree_model,
205 // g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
209 return G_TYPE_STRING;
213 __iter_has_child (GtkTreeModel *tree_model,
216 g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
222 __iter_parent (GtkTreeModel *tree_model,
226 g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
231 __tree_get_path (GtkTreeModel *tree_model,
234 // g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
235 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
236 g_return_val_if_fail (file->stamp == iter->stamp, FALSE);
238 gint n = GPOINTER_TO_INT (iter->user_data);
240 gint children = gtk_tree_model_iter_n_children (file->child, NULL);
242 if (n >= children - file->first_line)
245 return gtk_tree_path_new_from_indices (n, -1);
250 __iter_children (GtkTreeModel *tree_model,
254 g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
260 __tree_model_iter_n_children (GtkTreeModel *tree_model,
263 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
264 // g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
265 g_assert (iter == NULL);
267 gint children = gtk_tree_model_iter_n_children (file->child, NULL);
269 return children - file->first_line;
272 static GtkTreeModelFlags
273 __tree_model_get_flags (GtkTreeModel *model)
275 // g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
276 g_return_val_if_fail (PSPPIRE_IS_DELIMITED_TEXT (model), (GtkTreeModelFlags) 0);
278 return GTK_TREE_MODEL_LIST_ONLY;
282 __tree_model_get_n_columns (GtkTreeModel *tree_model)
284 // g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
285 PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (tree_model);
287 /* + 1 for the trailing field and +1 for the leading line number column */
288 return tf->max_delimiters + 1 + 1;
293 __iter_nth_child (GtkTreeModel *tree_model,
298 // g_print ("%s:%d %s %d\n", __FILE__, __LINE__, __FUNCTION__, n);
299 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
301 g_assert (parent == NULL);
303 g_return_val_if_fail (file, FALSE);
305 gint children = gtk_tree_model_iter_n_children (file->child, NULL);
307 if (n >= children - file->first_line)
310 iter->user_data = NULL;
314 iter->user_data = GINT_TO_POINTER (n);
315 iter->stamp = file->stamp;
321 /* Split row N into it's delimited fields (if it is not already cached)
322 and set this row as the current cache. */
324 split_row_into_fields (PsppireDelimitedText *file, gint n)
326 if (n == file->cache_row) /* Cache hit */
331 memset (file->cache_starts, 0, 512);
333 if (file->const_cache.string)
335 ss_dealloc (&file->const_cache);
337 ss_alloc_substring_pool (&file->const_cache,
338 PSPPIRE_TEXT_FILE (file->child)->lines[n], NULL);
339 struct substring cs = file->const_cache;
341 file->cache_starts[0] = cs.string;
343 UINT32_MAX != ss_first_mb (cs);
346 ucs4_t xx = ss_first_mb (cs);
348 for (del = file->delimiters; del; del = g_slist_next (del))
350 if (xx == GPOINTER_TO_INT (del->data))
353 int char_len = ss_first_mblen (cs);
354 file->cache_starts[field] = cs.string + char_len;
357 cs.string[char_len - 1] = '\0';
369 psppire_delimited_text_get_header_title (PsppireDelimitedText *file, gint column)
371 if (file->first_line <= 0)
374 split_row_into_fields (file, file->first_line - 1);
376 return file->cache_starts [column];
380 __get_value (GtkTreeModel *tree_model,
385 // g_print ("%s:%d %s Col: %d\n", __FILE__, __LINE__, __FUNCTION__, column);
386 PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model);
388 g_return_if_fail (iter->stamp == file->stamp);
390 gint n = GPOINTER_TO_INT (iter->user_data) + file->first_line;
392 // g_print ("%s:%d Row: %d\n", __FILE__, __LINE__, n);
396 g_value_init (value, G_TYPE_INT);
397 g_value_set_int (value, n + 1);
401 g_value_init (value, G_TYPE_STRING);
403 split_row_into_fields (file, n);
405 g_value_set_string (value, file->cache_starts [column - 1]);
410 __tree_model_init (GtkTreeModelIface *iface)
412 iface->get_flags = __tree_model_get_flags;
413 iface->get_n_columns = __tree_model_get_n_columns ;
414 iface->get_column_type = __tree_get_column_type;
415 iface->get_iter = __tree_get_iter;
416 iface->iter_next = __tree_iter_next;
417 iface->get_path = __tree_get_path;
418 iface->get_value = __get_value;
420 iface->iter_children = __iter_children;
421 iface->iter_has_child = __iter_has_child;
422 iface->iter_n_children = __tree_model_iter_n_children;
423 iface->iter_nth_child = __iter_nth_child;
424 iface->iter_parent = __iter_parent;
429 psppire_delimited_text_get_type (void)
431 static GType text_file_type = 0;
435 static const GTypeInfo text_file_info =
437 sizeof (PsppireDelimitedTextClass),
438 NULL, /* base_init */
439 NULL, /* base_finalize */
440 (GClassInitFunc) psppire_delimited_text_class_init,
441 NULL, /* class_finalize */
442 NULL, /* class_data */
443 sizeof (PsppireDelimitedText),
445 (GInstanceInitFunc) psppire_delimited_text_init,
448 static const GInterfaceInfo tree_model_info = {
449 (GInterfaceInitFunc) __tree_model_init,
454 text_file_type = g_type_register_static (G_TYPE_OBJECT,
455 "PsppireDelimitedText",
458 g_type_add_interface_static (text_file_type, GTK_TYPE_TREE_MODEL,
462 return text_file_type;
467 psppire_delimited_text_class_init (PsppireDelimitedTextClass *class)
469 GObjectClass *object_class;
471 parent_class = g_type_class_peek_parent (class);
472 object_class = (GObjectClass*) class;
474 GParamSpec *first_line_spec =
475 g_param_spec_int ("first-line",
477 P_("The first line to be considered."),
481 GParamSpec *delimiters_spec =
482 g_param_spec_pointer ("delimiters",
484 P_("A GSList of gunichars which delimit the fields."),
487 GParamSpec *child_spec =
488 g_param_spec_object ("child",
490 P_("The GtkTextModel which this object wraps."),
492 G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
494 object_class->set_property = psppire_delimited_text_set_property;
495 object_class->get_property = psppire_delimited_text_get_property;
497 g_object_class_install_property (object_class,
501 g_object_class_install_property (object_class,
505 g_object_class_install_property (object_class,
509 object_class->finalize = psppire_delimited_text_finalize;
510 object_class->dispose = psppire_delimited_text_dispose;
515 psppire_delimited_text_init (PsppireDelimitedText *text_file)
517 text_file->child = NULL;
518 text_file->first_line = 0;
519 text_file->delimiters = g_slist_prepend (NULL, GINT_TO_POINTER (':'));
521 text_file->const_cache.string = NULL;
522 text_file->const_cache.length = 0;
523 text_file->cache_row = -1;
524 memset (text_file->cache_starts, 0, 512);
526 text_file->max_delimiters = 0;
528 text_file->dispose_has_run = FALSE;
529 text_file->stamp = g_random_int ();
534 psppire_delimited_text_new (GtkTreeModel *child)
536 PsppireDelimitedText *retval =
537 g_object_new (PSPPIRE_TYPE_DELIMITED_TEXT,
545 psppire_delimited_text_finalize (GObject *object)
547 PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (object);
549 g_slist_free (tf->delimiters);
551 ss_dealloc (&tf->const_cache);
554 (* parent_class->finalize) (object);
559 psppire_delimited_text_dispose (GObject *object)
561 PsppireDelimitedText *ds = PSPPIRE_DELIMITED_TEXT (object);
563 if (ds->dispose_has_run)
567 (* parent_class->dispose) (object);
569 ds->dispose_has_run = TRUE;