1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2017, 2020 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-text-file.h"
27 #include "libpspp/line-reader.h"
28 #include "libpspp/message.h"
29 #include "libpspp/str.h"
30 #include "libpspp/i18n.h"
44 enum {MAX_LINE_LEN = 16384}; /* Max length of an acceptable line. */
47 read_lines (PsppireTextFile *tf)
49 if (tf->file_name && 0 != g_strcmp0 ("unset", tf->encoding))
51 struct line_reader *reader = line_reader_for_file (tf->encoding, tf->file_name, O_RDONLY);
55 msg_error (errno, _("Could not open `%s'"), tf->file_name);
60 ds_init_empty (&input);
61 for (tf->line_cnt = 0; tf->line_cnt < MAX_PREVIEW_LINES; tf->line_cnt++)
64 if (!line_reader_read (reader, &input, MAX_LINE_LEN + 1)
65 || ds_length (&input) > MAX_LINE_LEN)
68 if (line_reader_eof (reader))
70 else if (line_reader_error (reader))
71 msg (ME, _("Error reading `%s': %s"),
72 tf->file_name, strerror (line_reader_error (reader)));
74 msg (ME, _("Failed to read `%s', because it contains a line "
75 "over %d bytes long and therefore appears not to be "
77 tf->file_name, MAX_LINE_LEN);
78 line_reader_close (reader);
79 for (i = 0; i < tf->line_cnt; i++)
80 g_free (tf->lines[i].string);
86 tf->lines[tf->line_cnt]
87 = recode_substring_pool ("UTF-8",
88 line_reader_get_encoding (reader),
93 if (tf->line_cnt == 0)
96 msg (ME, _("`%s' is empty."), tf->file_name);
97 line_reader_close (reader);
98 for (i = 0; i < tf->line_cnt; i++)
99 g_free (tf->lines[i].string);
104 if (tf->line_cnt < MAX_PREVIEW_LINES)
106 tf->total_lines = tf->line_cnt;
107 tf->total_is_exact = true;
111 /* Estimate the number of lines in the file. */
113 off_t position = line_reader_tell (reader);
114 if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0)
116 tf->total_lines = (double) tf->line_cnt / position * s.st_size;
117 tf->total_is_exact = false;
122 tf->total_is_exact = true;
126 line_reader_close (reader);
131 psppire_text_file_set_property (GObject *object,
136 PsppireTextFile *tf = PSPPIRE_TEXT_FILE (object);
140 case PROP_MAXIMUM_LINES:
141 tf->maximum_lines = g_value_get_int (value);
144 tf->file_name = g_value_dup_string (value);
148 g_free (tf->encoding);
149 tf->encoding = g_value_dup_string (value);
153 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160 psppire_text_file_get_property (GObject *object,
165 PsppireTextFile *text_file = PSPPIRE_TEXT_FILE (object);
169 case PROP_MAXIMUM_LINES:
170 g_value_set_int (value, text_file->maximum_lines);
172 case PROP_LINE_COUNT:
173 g_value_set_int (value, text_file->line_cnt);
176 g_value_set_string (value, text_file->file_name);
179 g_value_set_string (value, text_file->encoding);
182 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
188 static void psppire_text_file_init (PsppireTextFile *text_file);
189 static void psppire_text_file_class_init (PsppireTextFileClass *class);
191 static void psppire_text_file_finalize (GObject *object);
192 static void psppire_text_file_dispose (GObject *object);
194 static GObjectClass *parent_class = NULL;
197 __tree_get_iter (GtkTreeModel *tree_model,
201 PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model);
206 gint *indices = gtk_tree_path_get_indices (path);
210 if (n >= file->line_cnt)
213 iter->user_data = GINT_TO_POINTER (n);
214 iter->stamp = file->stamp;
221 __tree_iter_next (GtkTreeModel *tree_model,
224 PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model);
225 g_return_val_if_fail (file->stamp == iter->stamp, FALSE);
227 gint n = GPOINTER_TO_INT (iter->user_data) + 1;
229 if (n >= file->line_cnt)
232 iter->user_data = GINT_TO_POINTER (n);
239 __tree_get_column_type (GtkTreeModel *tree_model,
245 return G_TYPE_STRING;
249 __iter_has_child (GtkTreeModel *tree_model,
257 __iter_parent (GtkTreeModel *tree_model,
265 __tree_get_path (GtkTreeModel *tree_model,
268 PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model);
269 g_return_val_if_fail (file->stamp == iter->stamp, FALSE);
271 gint n = GPOINTER_TO_INT (iter->user_data);
273 return gtk_tree_path_new_from_indices (n, -1);
278 __iter_children (GtkTreeModel *tree_model,
287 __tree_model_iter_n_children (GtkTreeModel *tree_model,
290 PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model);
291 g_assert (iter == NULL);
292 return file->line_cnt;
295 static GtkTreeModelFlags
296 __tree_model_get_flags (GtkTreeModel *model)
298 g_return_val_if_fail (PSPPIRE_IS_TEXT_FILE (model), (GtkTreeModelFlags) 0);
300 return GTK_TREE_MODEL_LIST_ONLY;
304 __tree_model_get_n_columns (GtkTreeModel *tree_model)
311 __iter_nth_child (GtkTreeModel *tree_model,
316 PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model);
318 g_assert (parent == NULL);
320 g_return_val_if_fail (file, FALSE);
322 if (n >= file->line_cnt)
325 iter->user_data = NULL;
329 iter->user_data = GINT_TO_POINTER (n);
330 iter->stamp = file->stamp;
337 __get_value (GtkTreeModel *tree_model,
342 PsppireTextFile *file = PSPPIRE_TEXT_FILE (tree_model);
344 g_return_if_fail (iter->stamp == file->stamp);
346 gint n = GPOINTER_TO_INT (iter->user_data);
348 g_return_if_fail (n < file->line_cnt);
352 g_value_init (value, G_TYPE_INT);
353 g_value_set_int (value, n + 1);
357 g_value_init (value, G_TYPE_STRING);
361 char *s = ss_xstrdup (file->lines[n]);
362 g_value_set_string (value, s);
367 g_assert_not_reached ();
372 __tree_model_init (GtkTreeModelIface *iface)
374 iface->get_flags = __tree_model_get_flags;
375 iface->get_n_columns = __tree_model_get_n_columns ;
376 iface->get_column_type = __tree_get_column_type;
377 iface->get_iter = __tree_get_iter;
378 iface->iter_next = __tree_iter_next;
379 iface->get_path = __tree_get_path;
380 iface->get_value = __get_value;
382 iface->iter_children = __iter_children;
383 iface->iter_has_child = __iter_has_child;
384 iface->iter_n_children = __tree_model_iter_n_children;
385 iface->iter_nth_child = __iter_nth_child;
386 iface->iter_parent = __iter_parent;
391 psppire_text_file_get_type (void)
393 static GType text_file_type = 0;
397 static const GTypeInfo text_file_info =
399 sizeof (PsppireTextFileClass),
400 NULL, /* base_init */
401 NULL, /* base_finalize */
402 (GClassInitFunc) psppire_text_file_class_init,
403 NULL, /* class_finalize */
404 NULL, /* class_data */
405 sizeof (PsppireTextFile),
407 (GInstanceInitFunc) psppire_text_file_init,
410 static const GInterfaceInfo tree_model_info = {
411 (GInterfaceInitFunc) __tree_model_init,
416 text_file_type = g_type_register_static (G_TYPE_OBJECT,
420 g_type_add_interface_static (text_file_type, GTK_TYPE_TREE_MODEL,
424 return text_file_type;
429 psppire_text_file_class_init (PsppireTextFileClass *class)
431 GObjectClass *object_class;
433 parent_class = g_type_class_peek_parent (class);
434 object_class = (GObjectClass*) class;
436 GParamSpec *maximum_lines_spec =
437 g_param_spec_int ("maximum-lines",
439 P_("An upper limit on the number of lines to consider"),
440 0, G_MAXINT, G_MAXINT,
443 GParamSpec *line_count_spec =
444 g_param_spec_int ("line-count",
446 P_("The number of lines in the file"),
447 0, G_MAXINT, G_MAXINT,
450 GParamSpec *file_name_spec =
451 g_param_spec_string ("file-name",
453 P_("The name of the file from which this object was constructed"),
455 G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
457 GParamSpec *encoding_spec =
458 g_param_spec_string ("encoding",
459 "Character Encoding",
460 P_("The character encoding of the file from which this object was constructed"),
462 G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
464 object_class->set_property = psppire_text_file_set_property;
465 object_class->get_property = psppire_text_file_get_property;
467 g_object_class_install_property (object_class,
471 g_object_class_install_property (object_class,
475 g_object_class_install_property (object_class,
479 g_object_class_install_property (object_class,
483 object_class->finalize = psppire_text_file_finalize;
484 object_class->dispose = psppire_text_file_dispose;
488 psppire_text_file_init (PsppireTextFile *text_file)
490 text_file->encoding = g_strdup ("unset");
491 text_file->file_name = NULL;
493 text_file->dispose_has_run = FALSE;
494 text_file->stamp = g_random_int ();
499 psppire_text_file_new (const gchar *file_name, const gchar *encoding)
501 PsppireTextFile *retval =
502 g_object_new (PSPPIRE_TYPE_TEXT_FILE,
503 "file-name", file_name,
504 "encoding", encoding,
511 psppire_text_file_finalize (GObject *object)
513 PsppireTextFile *tf = PSPPIRE_TEXT_FILE (object);
515 for (int i = 0; i < tf->line_cnt; i++)
516 g_free (tf->lines[i].string);
518 g_free (tf->encoding);
519 g_free (tf->file_name);
522 (* parent_class->finalize) (object);
527 psppire_text_file_dispose (GObject *object)
529 PsppireTextFile *ds = PSPPIRE_TEXT_FILE (object);
531 if (ds->dispose_has_run)
535 (* parent_class->dispose) (object);
537 ds->dispose_has_run = TRUE;
541 psppire_text_file_get_total_exact (PsppireTextFile *tf)
543 return tf->total_is_exact;
547 psppire_text_file_get_n_lines (PsppireTextFile *tf)
549 return tf->total_lines;