1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2015, 2016, 2017, 2018, 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 #include "psppire-import-textfile.h"
22 #include "libpspp/i18n.h"
23 #include "libpspp/line-reader.h"
24 #include "libpspp/message.h"
25 #include "libpspp/hmap.h"
26 #include "libpspp/hash-functions.h"
27 #include "libpspp/str.h"
28 #include "libpspp/misc.h"
30 #include "data/casereader.h"
31 #include "data/casereader-provider.h"
32 #include "data/data-in.h"
33 #include "data/format-guesser.h"
34 #include "data/value-labels.h"
36 #include "builder-wrapper.h"
38 #include "psppire-data-store.h"
39 #include "psppire-scanf.h"
41 #include "ui/syntax-gen.h"
44 #define _(msgid) gettext (msgid)
45 #define N_(msgid) msgid
47 /* Chooses a name for each column on the separators page */
48 static void choose_column_names (PsppireImportAssistant *ia);
50 /* Revises the contents of the fields tree view based on the
51 currently chosen set of separators and quotes. */
53 revise_fields_preview (PsppireImportAssistant *ia)
55 GSList *delimiters = NULL;
56 for (int i = 0; i < N_SEPARATORS; i++)
58 const struct separator *s = &separators[i];
59 GtkWidget *button = get_widget_assert (ia->text_builder, s->name);
60 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
62 delimiters = g_slist_prepend (delimiters, GINT_TO_POINTER (s->c));
66 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
67 GtkToggleButton *quote_cb = GTK_TOGGLE_BUTTON (ia->quote_cb);
68 const gchar *quotes = (gtk_toggle_button_get_active (quote_cb)
69 ? gtk_combo_box_text_get_active_text (cbt)
72 g_object_set (ia->delimiters_model,
73 "delimiters", delimiters,
77 choose_column_names (ia);
81 struct separator_count_node
83 struct hmap_node node;
84 int occurance; /* The number of times the separator occurs in a line */
85 int quantity; /* The number of lines with this occurance */
89 /* Picks the most likely separator and quote characters based on
92 choose_likely_separators (PsppireImportAssistant *ia)
95 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
100 struct hmap count_map[N_SEPARATORS];
101 for (int j = 0; j < N_SEPARATORS; ++j)
102 hmap_init (count_map + j);
104 GtkTreePath *p = gtk_tree_path_new_from_indices (first_line, -1);
106 for (valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (ia->text_file), &iter, p);
108 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->text_file), &iter))
110 gchar *line_text = NULL;
111 gtk_tree_model_get (GTK_TREE_MODEL (ia->text_file), &iter, 1, &line_text, -1);
113 gint *counts = XCALLOC (N_SEPARATORS, gint);
115 struct substring cs = ss_cstr (line_text);
117 UINT32_MAX != ss_first_mb (cs);
120 ucs4_t character = ss_first_mb (cs);
123 for (s = 0; s < N_SEPARATORS; ++s)
125 if (character == separators[s].c)
131 for (j = 0; j < N_SEPARATORS; ++j)
135 struct separator_count_node *cn = NULL;
136 unsigned int hash = hash_int (counts[j], 0);
137 HMAP_FOR_EACH_WITH_HASH (cn, struct separator_count_node, node, hash, &count_map[j])
139 if (cn->occurance == counts[j])
145 struct separator_count_node *new_cn = XZALLOC (struct separator_count_node);
146 new_cn->occurance = counts[j];
147 new_cn->quantity = 1;
148 hmap_insert (&count_map[j], &new_cn->node, hash);
158 gtk_tree_path_free (p);
160 if (hmap_count (count_map) > 0)
162 int most_frequent = -1;
164 for (int j = 0; j < N_SEPARATORS; ++j)
166 struct separator_count_node *cn;
167 struct separator_count_node *next;
168 HMAP_FOR_EACH_SAFE (cn, next, struct separator_count_node, node, &count_map[j])
170 if (largest < cn->quantity)
172 largest = cn->quantity;
177 hmap_destroy (&count_map[j]);
180 g_return_if_fail (most_frequent >= 0);
183 get_widget_assert (ia->text_builder, separators[most_frequent].name);
184 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
189 repopulate_delimiter_columns (PsppireImportAssistant *ia)
191 /* Remove all the columns */
192 while (gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view)) > 0)
194 GtkTreeViewColumn *tvc = gtk_tree_view_get_column (GTK_TREE_VIEW (ia->fields_tree_view), 0);
195 gtk_tree_view_remove_column (GTK_TREE_VIEW (ia->fields_tree_view), tvc);
199 gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model));
201 /* ... and put them back again. */
203 for (f = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view));
206 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
208 const gchar *title = NULL;
214 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
217 psppire_delimited_text_get_header_title
218 (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), f - 1);
224 GtkTreeViewColumn *column =
225 gtk_tree_view_column_new_with_attributes (title,
229 g_object_set (column,
231 "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
234 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->fields_tree_view), column);
239 reset_tree_view_model (PsppireImportAssistant *ia)
241 GtkTreeModel *tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ia->fields_tree_view));
243 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), NULL);
246 repopulate_delimiter_columns (ia);
248 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), tm);
249 // gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ia->fields_tree_view));
254 /* Resets IA's intro page to its initial state. */
256 reset_intro_page (PsppireImportAssistant *ia)
258 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->n_cases_button),
260 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->percent_button),
262 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->all_cases_button),
265 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ia->n_cases_spin), 1);
266 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ia->percent_spin), 0);
269 /* Called when one of the radio buttons is clicked. */
271 on_intro_amount_changed (PsppireImportAssistant *ia)
273 gtk_widget_set_sensitive (ia->n_cases_spin,
274 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)));
276 gtk_widget_set_sensitive (ia->percent_spin,
277 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)));
281 on_treeview_selection_change (PsppireImportAssistant *ia)
283 GtkTreeSelection *selection =
284 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
285 GtkTreeModel *model = NULL;
287 if (gtk_tree_selection_get_selected (selection, &model, &iter))
291 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
292 gint *index = gtk_tree_path_get_indices (path);
294 gtk_tree_path_free (path);
295 g_object_get (model, "maximum-lines", &max_lines, NULL);
296 gtk_widget_set_sensitive (ia->variable_names_cb,
297 (n > 0 && n < max_lines));
298 ia->delimiters_model =
299 psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file));
300 g_object_set (ia->delimiters_model, "first-line", n, NULL);
305 render_text_preview_line (GtkTreeViewColumn *tree_column,
306 GtkCellRenderer *cell,
307 GtkTreeModel *tree_model,
312 Set the text to a "insensitive" state if the row
313 is greater than what the user declared to be the maximum.
315 GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
316 gint *ii = gtk_tree_path_get_indices (path);
318 g_object_get (tree_model, "maximum-lines", &max_lines, NULL);
319 g_object_set (cell, "sensitive", (*ii < max_lines), NULL);
320 gtk_tree_path_free (path);
323 /* Resets IA's "first line" page to its initial state. */
325 reset_first_line_page (PsppireImportAssistant *ia)
327 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb), FALSE);
329 GtkTreeSelection *selection =
330 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
332 gtk_tree_selection_unselect_all (selection);
335 /* Initializes IA's first_line substructure. */
337 first_line_page_create (PsppireImportAssistant *ia)
339 GtkWidget *w = get_widget_assert (ia->text_builder, "FirstLine");
341 g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change);
342 g_object_set_data (G_OBJECT (w), "on-reset", reset_first_line_page);
344 add_page_to_assistant (ia, w,
345 GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line"));
347 GtkWidget *scrolled_window = get_widget_assert (ia->text_builder, "first-line-scroller");
349 if (ia->first_line_tree_view == NULL)
351 ia->first_line_tree_view = gtk_tree_view_new ();
352 g_object_set (ia->first_line_tree_view, "enable-search", FALSE, NULL);
354 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE);
356 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
357 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer,
361 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
362 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
364 renderer = gtk_cell_renderer_text_new ();
365 column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL);
366 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
368 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
370 g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed",
371 G_CALLBACK (on_treeview_selection_change), ia);
372 gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view);
375 gtk_widget_show_all (scrolled_window);
377 ia->variable_names_cb = get_widget_assert (ia->text_builder, "variable-names");
379 reset_first_line_page (ia);
383 intro_on_leave (PsppireImportAssistant *ia, GtkWidget *page, enum IMPORT_ASSISTANT_DIRECTION dir)
385 if (dir != IMPORT_ASSISTANT_FORWARDS)
389 g_object_get (ia->text_file, "line-count", &lc, NULL);
390 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
392 gint max_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin));
393 g_object_set (ia->text_file, "maximum-lines", max_lines, NULL);
395 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
397 gdouble percent = gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin));
398 g_object_set (ia->text_file, "maximum-lines", (gint) (lc * percent / 100.0), NULL);
402 g_object_set (ia->text_file, "maximum-lines", lc, NULL);
408 intro_on_enter (PsppireImportAssistant *ia, GtkWidget *page, enum IMPORT_ASSISTANT_DIRECTION dir)
410 GtkBuilder *builder = ia->text_builder;
411 GtkWidget *table = get_widget_assert (builder, "button-table");
416 ds_put_cstr (&s, _("This assistant will guide you through the process of "
417 "importing data into PSPP from a text file with one line "
418 "per case, in which fields are separated by tabs, "
419 "commas, or other delimiters.\n\n"));
423 if (ia->text_file->total_is_exact)
426 &s, ngettext ("The selected file contains %'lu line of text. ",
427 "The selected file contains %'lu lines of text. ",
428 ia->text_file->total_lines),
429 ia->text_file->total_lines);
431 else if (ia->text_file->total_lines > 0)
435 "The selected file contains approximately %'lu line of text. ",
436 "The selected file contains approximately %'lu lines of text. ",
437 ia->text_file->total_lines),
438 ia->text_file->total_lines);
441 "Only the first %zu line of the file will be shown for "
442 "preview purposes in the following screens. ",
443 "Only the first %zu lines of the file will be shown for "
444 "preview purposes in the following screens. ",
445 ia->text_file->n_lines),
446 ia->text_file->n_lines);
450 ds_put_cstr (&s, _("You may choose below how much of the file should "
451 "actually be imported."));
453 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
457 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 1) == NULL)
459 GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin);
460 gtk_grid_attach (GTK_GRID (table), hbox_n_cases,
465 GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin));
466 gtk_adjustment_set_lower (adj, 1.0);
468 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 2) == NULL)
470 GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"),
473 gtk_grid_attach (GTK_GRID (table), hbox_percent,
478 gtk_widget_show_all (table);
481 if (dir != IMPORT_ASSISTANT_FORWARDS)
484 reset_intro_page (ia);
485 on_intro_amount_changed (ia);
488 /* Initializes IA's intro substructure. */
490 intro_page_create (PsppireImportAssistant *ia)
492 GtkBuilder *builder = ia->text_builder;
494 GtkWidget *w = get_widget_assert (builder, "Intro");
496 ia->percent_spin = gtk_spin_button_new_with_range (0, 100, 1);
498 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Select the Lines to Import"));
500 ia->all_cases_button = get_widget_assert (builder, "import-all-cases");
501 ia->n_cases_button = get_widget_assert (builder, "import-n-cases");
502 ia->percent_button = get_widget_assert (builder, "import-percent");
504 g_signal_connect_swapped (ia->all_cases_button, "toggled",
505 G_CALLBACK (on_intro_amount_changed), ia);
506 g_signal_connect_swapped (ia->n_cases_button, "toggled",
507 G_CALLBACK (on_intro_amount_changed), ia);
508 g_signal_connect_swapped (ia->percent_button, "toggled",
509 G_CALLBACK (on_intro_amount_changed), ia);
511 g_object_set_data (G_OBJECT (w), "on-leaving", intro_on_leave);
512 g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter);
513 g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page);
519 /* Chooses a name for each column on the separators page */
521 choose_column_names (PsppireImportAssistant *ia)
524 unsigned long int generated_name_count = 0;
525 char *encoding = NULL;
526 g_object_get (ia->text_file, "encoding", &encoding, NULL);
528 dict_unref (ia->dict);
529 ia->dict = dict_create (encoding ? encoding : UTF8);
533 i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)) - 1;
536 const gchar *candidate_name = NULL;
538 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
540 candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i);
543 char *name = dict_make_unique_var_name (ia->dict,
545 &generated_name_count);
547 dict_create_var_assert (ia->dict, name, 0);
552 /* Called when the user toggles one of the separators
555 on_separator_toggle (GtkToggleButton *toggle UNUSED,
556 PsppireImportAssistant *ia)
558 revise_fields_preview (ia);
562 /* Called when the user changes the entry field for custom
565 on_separators_custom_entry_notify (GObject *gobject UNUSED,
566 GParamSpec *arg1 UNUSED,
567 PsppireImportAssistant *ia)
569 revise_fields_preview (ia);
572 /* Called when the user toggles the checkbox that enables custom
575 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
576 PsppireImportAssistant *ia)
578 bool is_active = gtk_toggle_button_get_active (custom_cb);
579 gtk_widget_set_sensitive (ia->custom_entry, is_active);
580 revise_fields_preview (ia);
583 /* Called when the user changes the selection in the combo box
584 that selects a quote character. */
586 on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia)
588 revise_fields_preview (ia);
591 /* Called when the user toggles the checkbox that enables
594 on_quote_cb_toggle (GtkToggleButton *quote_cb, PsppireImportAssistant *ia)
596 bool is_active = gtk_toggle_button_get_active (quote_cb);
597 gtk_widget_set_sensitive (ia->quote_combo, is_active);
598 revise_fields_preview (ia);
602 /* Called when the Reset button is clicked. */
604 reset_separators_page (PsppireImportAssistant *ia)
606 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->custom_cb), FALSE);
607 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->quote_cb), TRUE);
608 gtk_entry_set_text (GTK_ENTRY (ia->custom_entry), "");
610 for (gint i = 0; i < N_SEPARATORS; i++)
612 const struct separator *s = &separators[i];
613 GtkWidget *button = get_widget_assert (ia->text_builder, s->name);
614 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE);
617 if (ia->delimiters_model)
619 repopulate_delimiter_columns (ia);
621 revise_fields_preview (ia);
622 choose_likely_separators (ia);
626 /* Called just before the separators page becomes visible in the
629 prepare_separators_page (PsppireImportAssistant *ia, GtkWidget *new_page, enum IMPORT_ASSISTANT_DIRECTION dir)
631 if (dir != IMPORT_ASSISTANT_FORWARDS)
634 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view),
635 GTK_TREE_MODEL (ia->delimiters_model));
637 g_signal_connect_swapped (GTK_TREE_MODEL (ia->delimiters_model), "notify::delimiters",
638 G_CALLBACK (reset_tree_view_model), ia);
641 reset_separators_page (ia);
645 /* Initializes IA's separators substructure. */
647 separators_page_create (PsppireImportAssistant *ia)
649 GtkBuilder *builder = ia->text_builder;
653 GtkWidget *w = get_widget_assert (builder, "Separators");
655 g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
656 g_object_set_data (G_OBJECT (w), "on-reset", reset_separators_page);
658 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
660 ia->custom_cb = get_widget_assert (builder, "custom-cb");
661 ia->custom_entry = get_widget_assert (builder, "custom-entry");
662 ia->quote_combo = get_widget_assert (builder, "quote-combo");
663 ia->quote_cb = get_widget_assert (builder, "quote-cb");
665 gtk_widget_set_sensitive (ia->custom_entry,
666 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->custom_cb)));
668 gtk_combo_box_set_active (GTK_COMBO_BOX (ia->quote_combo), 0);
669 gtk_entry_set_max_length (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (ia->quote_combo))), 2);
671 if (ia->fields_tree_view == NULL)
673 GtkWidget *scroller = get_widget_assert (ia->text_builder, "fields-scroller");
674 ia->fields_tree_view = gtk_tree_view_new ();
675 g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL);
676 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
677 gtk_widget_show_all (scroller);
680 g_signal_connect (ia->quote_combo, "changed",
681 G_CALLBACK (on_quote_combo_change), ia);
682 g_signal_connect (ia->quote_cb, "toggled",
683 G_CALLBACK (on_quote_cb_toggle), ia);
684 g_signal_connect (ia->custom_entry, "notify::text",
685 G_CALLBACK (on_separators_custom_entry_notify), ia);
686 g_signal_connect (ia->custom_cb, "toggled",
687 G_CALLBACK (on_separators_custom_cb_toggle), ia);
688 for (i = 0; i < N_SEPARATORS; i++)
689 g_signal_connect (get_widget_assert (builder, separators[i].name),
690 "toggled", G_CALLBACK (on_separator_toggle), ia);
692 reset_separators_page (ia);
697 static struct casereader_random_class my_casereader_class;
699 static struct ccase *
700 my_read (struct casereader *reader, void *aux, casenumber idx)
702 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
703 GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
705 GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
707 const struct caseproto *proto = casereader_get_proto (reader);
710 struct ccase *c = NULL;
711 if (gtk_tree_model_get_iter (tm, &iter, tp))
713 c = case_create (proto);
715 for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
718 gtk_tree_model_get_value (tm, &iter, i + 1, &value);
720 const struct variable *var = dict_get_var (ia->casereader_dict, i);
722 const gchar *ss = g_value_get_string (&value);
725 union value *v = case_data_rw (c, var);
726 /* In this reader we derive the union value from the
727 string in the tree_model. We retrieve the width and format
728 from a dictionary which is stored directly after
729 the reader creation. Changes in ia->dict in the
730 variable window are not reflected here and therefore
731 this is always compatible with the width in the
732 caseproto. See bug #58298 */
733 char *xx = data_in (ss_cstr (ss),
735 var_get_write_format (var)->type,
736 settings_get_fmt_settings (),
737 v, var_get_width (var), "UTF-8");
741 g_value_unset (&value);
745 gtk_tree_path_free (tp);
751 my_destroy (struct casereader *reader, void *aux)
753 g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
757 my_advance (struct casereader *reader, void *aux, casenumber n)
759 g_print ("%s:%d\n", __FILE__, __LINE__);
762 static struct casereader *
763 textfile_create_reader (PsppireImportAssistant *ia)
765 int n_vars = dict_get_n_vars (ia->dict);
769 struct fmt_guesser **fg = XCALLOC (n_vars, struct fmt_guesser *);
770 for (i = 0 ; i < n_vars; ++i)
772 fg[i] = fmt_guesser_create ();
775 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
779 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter);
781 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter))
783 for (i = 0 ; i < n_vars; ++i)
786 gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1);
788 fmt_guesser_add (fg[i], ss_cstr (s));
793 struct caseproto *proto = caseproto_create ();
794 for (i = 0 ; i < n_vars; ++i)
797 fmt_guesser_guess (fg[i], &fs);
799 fmt_fix (&fs, FMT_FOR_INPUT);
801 struct variable *var = dict_get_var (ia->dict, i);
803 int width = fmt_var_width (&fs);
805 var_set_width_and_formats (var, width,
808 proto = caseproto_add_width (proto, width);
809 fmt_guesser_destroy (fg[i]);
814 struct casereader *cr = casereader_create_random (proto, n_rows, &my_casereader_class, ia);
815 /* Store the dictionary at this point when the casereader is created.
816 my_read depends on the dictionary to interpret the strings in the treeview.
817 This guarantees that the union value is produced according to the
818 caseproto in the reader. */
819 ia->casereader_dict = dict_clone (ia->dict);
820 caseproto_unref (proto);
825 /* When during import the variable type is changed, the reader is reinitialized
826 based on the new dictionary with a fresh caseprototype. The default behaviour
827 when a variable type is changed and the column is resized is that the union
828 value is interpreted with new variable type and an overlay for that column
829 is generated. Here we reinit to the original reader based on strings.
830 As a result you can switch from string to numeric to string without loosing
831 the string information. */
833 ia_variable_changed_cb (GObject *obj, gint var_num, guint what,
834 const struct variable *oldvar, gpointer data)
836 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (data);
838 struct caseproto *proto = caseproto_create();
839 for (int i = 0; i < dict_get_n_vars (ia->dict); i++)
841 const struct variable *var = dict_get_var (ia->dict, i);
842 int width = var_get_width (var);
843 proto = caseproto_add_width (proto, width);
846 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
848 PsppireDataStore *store = NULL;
849 g_object_get (ia->data_sheet, "data-model", &store, NULL);
851 struct casereader *cr = casereader_create_random (proto, n_rows,
852 &my_casereader_class, ia);
853 psppire_data_store_set_reader (store, cr);
854 dict_unref (ia->casereader_dict);
855 ia->casereader_dict = dict_clone (ia->dict);
859 /* Set the data model for both the data sheet and the variable sheet. */
861 textfile_set_data_models (PsppireImportAssistant *ia)
863 my_casereader_class.read = my_read;
864 my_casereader_class.destroy = my_destroy;
865 my_casereader_class.advance = my_advance;
867 struct casereader *reader = textfile_create_reader (ia);
869 PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
870 PsppireDataStore *store = psppire_data_store_new (dict);
871 psppire_data_store_set_reader (store, reader);
872 g_signal_connect (dict, "variable-changed",
873 G_CALLBACK (ia_variable_changed_cb),
876 g_object_set (ia->data_sheet, "data-model", store, NULL);
877 g_object_set (ia->var_sheet, "data-model", dict, NULL);
881 first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s)
884 g_object_get (ia->delimiters_model, "first-line", &first_case, NULL);
887 ds_put_format (s, " /FIRSTCASE=%d\n", first_case + 1);
890 /* Emits PSPP syntax to S that applies the dictionary attributes
891 (such as missing values and value labels) of the variables in
894 apply_dict (const struct dictionary *dict, struct string *s)
896 size_t n_vars = dict_get_n_vars (dict);
898 for (size_t i = 0; i < n_vars; i++)
900 struct variable *var = dict_get_var (dict, i);
901 const char *name = var_get_name (var);
902 enum val_type type = var_get_type (var);
903 int width = var_get_width (var);
904 enum measure measure = var_get_measure (var);
905 enum var_role role = var_get_role (var);
906 enum alignment alignment = var_get_alignment (var);
907 const struct fmt_spec *format = var_get_print_format (var);
909 if (var_has_missing_values (var))
911 const struct missing_values *mv = var_get_missing_values (var);
914 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
915 for (j = 0; j < mv_n_values (mv); j++)
918 ds_put_cstr (s, ", ");
919 syntax_gen_value (s, mv_get_value (mv, j), width, format);
922 if (mv_has_range (mv))
925 if (mv_has_value (mv))
926 ds_put_cstr (s, ", ");
927 mv_get_range (mv, &low, &high);
928 syntax_gen_num_range (s, low, high, format);
930 ds_put_cstr (s, ").\n");
932 if (var_has_value_labels (var))
934 const struct val_labs *vls = var_get_value_labels (var);
935 const struct val_lab **labels = val_labs_sorted (vls);
936 size_t n_labels = val_labs_count (vls);
938 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
939 for (size_t j = 0; j < n_labels; j++)
941 const struct val_lab *vl = labels[j];
942 ds_put_cstr (s, "\n ");
943 syntax_gen_value (s, &vl->value, width, format);
944 ds_put_byte (s, ' ');
945 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
948 ds_put_cstr (s, ".\n");
950 if (var_has_label (var))
951 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
952 name, var_get_label (var));
953 if (measure != var_default_measure (type))
954 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
955 name, measure_to_syntax (measure));
956 if (role != ROLE_INPUT)
957 syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
958 var_role_to_syntax (role), name);
959 if (alignment != var_default_alignment (type))
960 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
961 name, alignment_to_syntax (alignment));
962 if (var_get_display_width (var) != var_default_display_width (width))
963 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
964 name, var_get_display_width (var));
970 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
973 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
975 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
976 ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n",
977 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line);
978 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
979 ds_put_format (s, "SAMPLE %.4g.\n",
980 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
985 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
987 g_return_if_fail (ia->dict);
989 ds_put_cstr (s, " /VARIABLES=\n");
991 int n_vars = dict_get_n_vars (ia->dict);
992 for (int i = 0; i < n_vars; i++)
994 struct variable *var = dict_get_var (ia->dict, i);
995 char format_string[FMT_STRING_LEN_MAX + 1];
996 fmt_to_string (var_get_print_format (var), format_string);
997 ds_put_format (s, " %s %s%s\n",
998 var_get_name (var), format_string,
999 i == n_vars - 1 ? "." : "");
1004 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1008 ds_put_cstr (s, " /DELIMITERS=\"");
1010 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->text_builder, "tab"))))
1011 ds_put_cstr (s, "\\t");
1012 for (i = 0; i < N_SEPARATORS; i++)
1014 const struct separator *seps = &separators[i];
1015 GtkWidget *button = get_widget_assert (ia->text_builder, seps->name);
1016 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1018 if (seps->c == '\t')
1021 ds_put_byte (s, seps->c);
1024 ds_put_cstr (s, "\"\n");
1026 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb)))
1028 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
1029 gchar *quotes = gtk_combo_box_text_get_active_text (cbt);
1030 if (quotes && *quotes)
1031 syntax_gen_pspp (s, " /QUALIFIER=%sq\n", quotes);
1038 text_spec_gen_syntax (PsppireImportAssistant *ia, struct string *s)
1040 gchar *file_name = NULL;
1041 gchar *encoding = NULL;
1042 g_object_get (ia->text_file,
1043 "file-name", &file_name,
1044 "encoding", &encoding,
1047 if (file_name == NULL)
1055 if (encoding && strcmp (encoding, "Auto"))
1056 syntax_gen_pspp (s, " /ENCODING=%sq\n", encoding);
1059 " /ARRANGEMENT=DELIMITED\n"
1060 " /DELCASE=LINE\n");
1062 first_line_append_syntax (ia, s);
1063 separators_append_syntax (ia, s);
1065 formats_append_syntax (ia, s);
1066 apply_dict (ia->dict, s);
1067 intro_append_syntax (ia, s);