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"
35 #include "builder-wrapper.h"
37 #include "psppire-data-store.h"
38 #include "psppire-scanf.h"
40 #include "ui/syntax-gen.h"
43 #define _(msgid) gettext (msgid)
44 #define N_(msgid) msgid
46 /* Chooses a name for each column on the separators page */
47 static void choose_column_names (PsppireImportAssistant *ia);
49 /* Revises the contents of the fields tree view based on the
50 currently chosen set of separators. */
52 revise_fields_preview (PsppireImportAssistant *ia)
54 choose_column_names (ia);
58 struct separator_count_node
60 struct hmap_node node;
61 int occurance; /* The number of times the separator occurs in a line */
62 int quantity; /* The number of lines with this occurance */
66 /* Picks the most likely separator and quote characters based on
69 choose_likely_separators (PsppireImportAssistant *ia)
72 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
78 struct hmap count_map[SEPARATOR_CNT];
79 for (j = 0; j < SEPARATOR_CNT; ++j)
80 hmap_init (count_map + j);
82 GtkTreePath *p = gtk_tree_path_new_from_indices (first_line, -1);
84 for (valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (ia->text_file), &iter, p);
86 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->text_file), &iter))
88 gchar *line_text = NULL;
89 gtk_tree_model_get (GTK_TREE_MODEL (ia->text_file), &iter, 1, &line_text, -1);
91 gint *counts = xzalloc (sizeof *counts * SEPARATOR_CNT);
93 struct substring cs = ss_cstr (line_text);
95 UINT32_MAX != ss_first_mb (cs);
98 ucs4_t character = ss_first_mb (cs);
101 for (s = 0; s < SEPARATOR_CNT; ++s)
103 if (character == separators[s].c)
109 for (j = 0; j < SEPARATOR_CNT; ++j)
113 struct separator_count_node *cn = NULL;
114 unsigned int hash = hash_int (counts[j], 0);
115 HMAP_FOR_EACH_WITH_HASH (cn, struct separator_count_node, node, hash, &count_map[j])
117 if (cn->occurance == counts[j])
123 struct separator_count_node *new_cn = xzalloc (sizeof *new_cn);
124 new_cn->occurance = counts[j];
125 new_cn->quantity = 1;
126 hmap_insert (&count_map[j], &new_cn->node, hash);
136 gtk_tree_path_free (p);
138 if (hmap_count (count_map) > 0)
140 int most_frequent = -1;
142 for (j = 0; j < SEPARATOR_CNT; ++j)
144 struct separator_count_node *cn;
145 struct separator_count_node *next;
146 HMAP_FOR_EACH_SAFE (cn, next, struct separator_count_node, node, &count_map[j])
148 if (largest < cn->quantity)
150 largest = cn->quantity;
155 hmap_destroy (&count_map[j]);
158 g_return_if_fail (most_frequent >= 0);
161 get_widget_assert (ia->text_builder, separators[most_frequent].name);
162 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
167 repopulate_delimiter_columns (PsppireImportAssistant *ia)
169 /* Remove all the columns */
170 while (gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view)) > 0)
172 GtkTreeViewColumn *tvc = gtk_tree_view_get_column (GTK_TREE_VIEW (ia->fields_tree_view), 0);
173 gtk_tree_view_remove_column (GTK_TREE_VIEW (ia->fields_tree_view), tvc);
177 gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model));
179 /* ... and put them back again. */
181 for (f = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view));
184 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
186 const gchar *title = NULL;
192 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
195 psppire_delimited_text_get_header_title
196 (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), f - 1);
202 GtkTreeViewColumn *column =
203 gtk_tree_view_column_new_with_attributes (title,
207 g_object_set (column,
209 "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
212 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->fields_tree_view), column);
217 reset_tree_view_model (PsppireImportAssistant *ia)
219 GtkTreeModel *tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ia->fields_tree_view));
221 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), NULL);
224 repopulate_delimiter_columns (ia);
226 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), tm);
227 // gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ia->fields_tree_view));
232 /* Resets IA's intro page to its initial state. */
234 reset_intro_page (PsppireImportAssistant *ia)
236 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->n_cases_button),
238 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->percent_button),
240 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->all_cases_button),
243 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ia->n_cases_spin), 1);
244 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ia->percent_spin), 0);
247 /* Called when one of the radio buttons is clicked. */
249 on_intro_amount_changed (PsppireImportAssistant *ia)
251 gtk_widget_set_sensitive (ia->n_cases_spin,
252 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)));
254 gtk_widget_set_sensitive (ia->percent_spin,
255 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)));
259 on_treeview_selection_change (PsppireImportAssistant *ia)
261 GtkTreeSelection *selection =
262 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
263 GtkTreeModel *model = NULL;
265 if (gtk_tree_selection_get_selected (selection, &model, &iter))
269 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
270 gint *index = gtk_tree_path_get_indices (path);
272 gtk_tree_path_free (path);
273 g_object_get (model, "maximum-lines", &max_lines, NULL);
274 gtk_widget_set_sensitive (ia->variable_names_cb,
275 (n > 0 && n < max_lines));
276 ia->delimiters_model =
277 psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file));
278 g_object_set (ia->delimiters_model, "first-line", n, NULL);
283 render_text_preview_line (GtkTreeViewColumn *tree_column,
284 GtkCellRenderer *cell,
285 GtkTreeModel *tree_model,
290 Set the text to a "insensitive" state if the row
291 is greater than what the user declared to be the maximum.
293 GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
294 gint *ii = gtk_tree_path_get_indices (path);
296 g_object_get (tree_model, "maximum-lines", &max_lines, NULL);
297 g_object_set (cell, "sensitive", (*ii < max_lines), NULL);
298 gtk_tree_path_free (path);
301 /* Resets IA's "first line" page to its initial state. */
303 reset_first_line_page (PsppireImportAssistant *ia)
305 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb), FALSE);
307 GtkTreeSelection *selection =
308 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
310 gtk_tree_selection_unselect_all (selection);
313 /* Initializes IA's first_line substructure. */
315 first_line_page_create (PsppireImportAssistant *ia)
317 GtkWidget *w = get_widget_assert (ia->text_builder, "FirstLine");
319 g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change);
320 g_object_set_data (G_OBJECT (w), "on-reset", reset_first_line_page);
322 add_page_to_assistant (ia, w,
323 GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line"));
325 GtkWidget *scrolled_window = get_widget_assert (ia->text_builder, "first-line-scroller");
327 if (ia->first_line_tree_view == NULL)
329 ia->first_line_tree_view = gtk_tree_view_new ();
330 g_object_set (ia->first_line_tree_view, "enable-search", FALSE, NULL);
332 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE);
334 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
335 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer,
339 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
340 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
342 renderer = gtk_cell_renderer_text_new ();
343 column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL);
344 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
346 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
348 g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed",
349 G_CALLBACK (on_treeview_selection_change), ia);
350 gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view);
353 gtk_widget_show_all (scrolled_window);
355 ia->variable_names_cb = get_widget_assert (ia->text_builder, "variable-names");
357 reset_first_line_page (ia);
361 intro_on_leave (PsppireImportAssistant *ia, GtkWidget *page, enum IMPORT_ASSISTANT_DIRECTION dir)
363 if (dir != IMPORT_ASSISTANT_FORWARDS)
367 g_object_get (ia->text_file, "line-count", &lc, NULL);
368 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
370 gint max_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin));
371 g_object_set (ia->text_file, "maximum-lines", max_lines, NULL);
373 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
375 gdouble percent = gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin));
376 g_object_set (ia->text_file, "maximum-lines", (gint) (lc * percent / 100.0), NULL);
380 g_object_set (ia->text_file, "maximum-lines", lc, NULL);
386 intro_on_enter (PsppireImportAssistant *ia, GtkWidget *page, enum IMPORT_ASSISTANT_DIRECTION dir)
388 GtkBuilder *builder = ia->text_builder;
389 GtkWidget *table = get_widget_assert (builder, "button-table");
394 ds_put_cstr (&s, _("This assistant will guide you through the process of "
395 "importing data into PSPP from a text file with one line "
396 "per case, in which fields are separated by tabs, "
397 "commas, or other delimiters.\n\n"));
401 if (ia->text_file->total_is_exact)
404 &s, ngettext ("The selected file contains %'lu line of text. ",
405 "The selected file contains %'lu lines of text. ",
406 ia->text_file->total_lines),
407 ia->text_file->total_lines);
409 else if (ia->text_file->total_lines > 0)
413 "The selected file contains approximately %'lu line of text. ",
414 "The selected file contains approximately %'lu lines of text. ",
415 ia->text_file->total_lines),
416 ia->text_file->total_lines);
419 "Only the first %zu line of the file will be shown for "
420 "preview purposes in the following screens. ",
421 "Only the first %zu lines of the file will be shown for "
422 "preview purposes in the following screens. ",
423 ia->text_file->line_cnt),
424 ia->text_file->line_cnt);
428 ds_put_cstr (&s, _("You may choose below how much of the file should "
429 "actually be imported."));
431 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
435 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 1) == NULL)
437 GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin);
438 gtk_grid_attach (GTK_GRID (table), hbox_n_cases,
443 GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin));
444 gtk_adjustment_set_lower (adj, 1.0);
446 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 2) == NULL)
448 GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"),
451 gtk_grid_attach (GTK_GRID (table), hbox_percent,
456 gtk_widget_show_all (table);
459 if (dir != IMPORT_ASSISTANT_FORWARDS)
462 reset_intro_page (ia);
463 on_intro_amount_changed (ia);
466 /* Initializes IA's intro substructure. */
468 intro_page_create (PsppireImportAssistant *ia)
470 GtkBuilder *builder = ia->text_builder;
472 GtkWidget *w = get_widget_assert (builder, "Intro");
474 ia->percent_spin = gtk_spin_button_new_with_range (0, 100, 1);
476 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Select the Lines to Import"));
478 ia->all_cases_button = get_widget_assert (builder, "import-all-cases");
479 ia->n_cases_button = get_widget_assert (builder, "import-n-cases");
480 ia->percent_button = get_widget_assert (builder, "import-percent");
482 g_signal_connect_swapped (ia->all_cases_button, "toggled",
483 G_CALLBACK (on_intro_amount_changed), ia);
484 g_signal_connect_swapped (ia->n_cases_button, "toggled",
485 G_CALLBACK (on_intro_amount_changed), ia);
486 g_signal_connect_swapped (ia->percent_button, "toggled",
487 G_CALLBACK (on_intro_amount_changed), ia);
489 g_object_set_data (G_OBJECT (w), "on-leaving", intro_on_leave);
490 g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter);
491 g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page);
497 /* Chooses a name for each column on the separators page */
499 choose_column_names (PsppireImportAssistant *ia)
502 unsigned long int generated_name_count = 0;
503 char *encoding = NULL;
504 g_object_get (ia->text_file, "encoding", &encoding, NULL);
506 dict_unref (ia->dict);
507 ia->dict = dict_create (encoding ? encoding : UTF8);
511 i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)) - 1;
514 const gchar *candidate_name = NULL;
516 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
518 candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i);
521 char *name = dict_make_unique_var_name (ia->dict,
523 &generated_name_count);
525 dict_create_var_assert (ia->dict, name, 0);
530 /* Called when the user toggles one of the separators
533 on_separator_toggle (GtkToggleButton *toggle UNUSED,
534 PsppireImportAssistant *ia)
537 GSList *delimiters = NULL;
538 for (i = 0; i < SEPARATOR_CNT; i++)
540 const struct separator *s = &separators[i];
541 GtkWidget *button = get_widget_assert (ia->text_builder, s->name);
542 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
544 delimiters = g_slist_prepend (delimiters, GINT_TO_POINTER (s->c));
548 g_object_set (ia->delimiters_model, "delimiters", delimiters, NULL);
550 revise_fields_preview (ia);
554 /* Called when the user changes the entry field for custom
557 on_separators_custom_entry_notify (GObject *gobject UNUSED,
558 GParamSpec *arg1 UNUSED,
559 PsppireImportAssistant *ia)
561 revise_fields_preview (ia);
564 /* Called when the user toggles the checkbox that enables custom
567 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
568 PsppireImportAssistant *ia)
570 bool is_active = gtk_toggle_button_get_active (custom_cb);
571 gtk_widget_set_sensitive (ia->custom_entry, is_active);
572 revise_fields_preview (ia);
575 /* Called when the user changes the selection in the combo box
576 that selects a quote character. */
578 on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia)
580 // revise_fields_preview (ia);
583 /* Called when the user toggles the checkbox that enables
586 on_quote_cb_toggle (GtkToggleButton *quote_cb, PsppireImportAssistant *ia)
588 bool is_active = gtk_toggle_button_get_active (quote_cb);
589 gtk_widget_set_sensitive (ia->quote_combo, is_active);
590 revise_fields_preview (ia);
594 /* Called when the Reset button is clicked. */
596 reset_separators_page (PsppireImportAssistant *ia)
598 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->custom_cb), FALSE);
599 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->quote_cb), FALSE);
600 gtk_entry_set_text (GTK_ENTRY (ia->custom_entry), "");
602 for (gint i = 0; i < SEPARATOR_CNT; i++)
604 const struct separator *s = &separators[i];
605 GtkWidget *button = get_widget_assert (ia->text_builder, s->name);
606 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE);
609 repopulate_delimiter_columns (ia);
611 revise_fields_preview (ia);
612 choose_likely_separators (ia);
615 /* Called just before the separators page becomes visible in the
618 prepare_separators_page (PsppireImportAssistant *ia)
620 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view),
621 GTK_TREE_MODEL (ia->delimiters_model));
623 g_signal_connect_swapped (GTK_TREE_MODEL (ia->delimiters_model), "notify::delimiters",
624 G_CALLBACK (reset_tree_view_model), ia);
627 reset_separators_page (ia);
631 /* Initializes IA's separators substructure. */
633 separators_page_create (PsppireImportAssistant *ia)
635 GtkBuilder *builder = ia->text_builder;
639 GtkWidget *w = get_widget_assert (builder, "Separators");
641 g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
642 g_object_set_data (G_OBJECT (w), "on-reset", reset_separators_page);
644 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
646 ia->custom_cb = get_widget_assert (builder, "custom-cb");
647 ia->custom_entry = get_widget_assert (builder, "custom-entry");
648 ia->quote_combo = get_widget_assert (builder, "quote-combo");
649 ia->quote_cb = get_widget_assert (builder, "quote-cb");
651 gtk_widget_set_sensitive (ia->custom_entry,
652 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->custom_cb)));
654 gtk_combo_box_set_active (GTK_COMBO_BOX (ia->quote_combo), 0);
656 if (ia->fields_tree_view == NULL)
658 GtkWidget *scroller = get_widget_assert (ia->text_builder, "fields-scroller");
659 ia->fields_tree_view = gtk_tree_view_new ();
660 g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL);
661 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
662 gtk_widget_show_all (scroller);
665 g_signal_connect (ia->quote_combo, "changed",
666 G_CALLBACK (on_quote_combo_change), ia);
667 g_signal_connect (ia->quote_cb, "toggled",
668 G_CALLBACK (on_quote_cb_toggle), ia);
669 g_signal_connect (ia->custom_entry, "notify::text",
670 G_CALLBACK (on_separators_custom_entry_notify), ia);
671 g_signal_connect (ia->custom_cb, "toggled",
672 G_CALLBACK (on_separators_custom_cb_toggle), ia);
673 for (i = 0; i < SEPARATOR_CNT; i++)
674 g_signal_connect (get_widget_assert (builder, separators[i].name),
675 "toggled", G_CALLBACK (on_separator_toggle), ia);
677 reset_separators_page (ia);
682 static struct casereader_random_class my_casereader_class;
684 static struct ccase *
685 my_read (struct casereader *reader, void *aux, casenumber idx)
687 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
688 GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
690 GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
692 const struct caseproto *proto = casereader_get_proto (reader);
695 struct ccase *c = NULL;
696 if (gtk_tree_model_get_iter (tm, &iter, tp))
698 c = case_create (proto);
700 for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
703 gtk_tree_model_get_value (tm, &iter, i + 1, &value);
705 const struct variable *var = dict_get_var (ia->casereader_dict, i);
707 const gchar *ss = g_value_get_string (&value);
710 union value *v = case_data_rw (c, var);
711 /* In this reader we derive the union value from the
712 string in the tree_model. We retrieve the width and format
713 from a dictionary which is stored directly after
714 the reader creation. Changes in ia->dict in the
715 variable window are not reflected here and therefore
716 this is always compatible with the width in the
717 caseproto. See bug #58298 */
718 char *xx = data_in (ss_cstr (ss),
720 var_get_write_format (var)->type,
721 v, var_get_width (var), "UTF-8");
725 g_value_unset (&value);
729 gtk_tree_path_free (tp);
735 my_destroy (struct casereader *reader, void *aux)
737 g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
741 my_advance (struct casereader *reader, void *aux, casenumber cnt)
743 g_print ("%s:%d\n", __FILE__, __LINE__);
746 static struct casereader *
747 textfile_create_reader (PsppireImportAssistant *ia)
749 int n_vars = dict_get_var_cnt (ia->dict);
753 struct fmt_guesser **fg = XCALLOC (n_vars, struct fmt_guesser *);
754 for (i = 0 ; i < n_vars; ++i)
756 fg[i] = fmt_guesser_create ();
759 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
763 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter);
765 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter))
767 for (i = 0 ; i < n_vars; ++i)
770 gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1);
772 fmt_guesser_add (fg[i], ss_cstr (s));
777 struct caseproto *proto = caseproto_create ();
778 for (i = 0 ; i < n_vars; ++i)
781 fmt_guesser_guess (fg[i], &fs);
783 fmt_fix (&fs, FMT_FOR_INPUT);
785 struct variable *var = dict_get_var (ia->dict, i);
787 int width = fmt_var_width (&fs);
789 var_set_width_and_formats (var, width,
792 proto = caseproto_add_width (proto, width);
793 fmt_guesser_destroy (fg[i]);
798 struct casereader *cr = casereader_create_random (proto, n_rows, &my_casereader_class, ia);
799 /* Store the dictionary at this point when the casereader is created.
800 my_read depends on the dictionary to interpret the strings in the treeview.
801 This guarantees that the union value is produced according to the
802 caseproto in the reader. */
803 ia->casereader_dict = dict_clone (ia->dict);
804 caseproto_unref (proto);
809 /* When during import the variable type is changed, the reader is reinitialized
810 based on the new dictionary with a fresh caseprototype. The default behaviour
811 when a variable type is changed and the column is resized is that the union
812 value is interpreted with new variable type and an overlay for that column
813 is generated. Here we reinit to the original reader based on strings.
814 As a result you can switch from string to numeric to string without loosing
815 the string information. */
817 ia_variable_changed_cb (GObject *obj, gint var_num, guint what,
818 const struct variable *oldvar, gpointer data)
820 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (data);
822 struct caseproto *proto = caseproto_create();
823 for (int i = 0; i < dict_get_var_cnt (ia->dict); i++)
825 const struct variable *var = dict_get_var (ia->dict, i);
826 int width = var_get_width (var);
827 proto = caseproto_add_width (proto, width);
830 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
832 PsppireDataStore *store = NULL;
833 g_object_get (ia->data_sheet, "data-model", &store, NULL);
835 struct casereader *cr = casereader_create_random (proto, n_rows,
836 &my_casereader_class, ia);
837 psppire_data_store_set_reader (store, cr);
838 dict_unref (ia->casereader_dict);
839 ia->casereader_dict = dict_clone (ia->dict);
843 /* Set the data model for both the data sheet and the variable sheet. */
845 textfile_set_data_models (PsppireImportAssistant *ia)
847 my_casereader_class.read = my_read;
848 my_casereader_class.destroy = my_destroy;
849 my_casereader_class.advance = my_advance;
851 struct casereader *reader = textfile_create_reader (ia);
853 PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
854 PsppireDataStore *store = psppire_data_store_new (dict);
855 psppire_data_store_set_reader (store, reader);
856 g_signal_connect (dict, "variable-changed",
857 G_CALLBACK (ia_variable_changed_cb),
860 g_object_set (ia->data_sheet, "data-model", store, NULL);
861 g_object_set (ia->var_sheet, "data-model", dict, NULL);