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. */
53 revise_fields_preview (PsppireImportAssistant *ia)
55 choose_column_names (ia);
59 struct separator_count_node
61 struct hmap_node node;
62 int occurance; /* The number of times the separator occurs in a line */
63 int quantity; /* The number of lines with this occurance */
67 /* Picks the most likely separator and quote characters based on
70 choose_likely_separators (PsppireImportAssistant *ia)
73 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
78 struct hmap count_map[SEPARATOR_CNT];
79 for (int 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 = XCALLOC (SEPARATOR_CNT, gint);
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 (struct separator_count_node);
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 (int 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->n_lines),
424 ia->text_file->n_lines);
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, GtkWidget *new_page, enum IMPORT_ASSISTANT_DIRECTION dir)
620 if (dir != IMPORT_ASSISTANT_FORWARDS)
623 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view),
624 GTK_TREE_MODEL (ia->delimiters_model));
626 g_signal_connect_swapped (GTK_TREE_MODEL (ia->delimiters_model), "notify::delimiters",
627 G_CALLBACK (reset_tree_view_model), ia);
630 reset_separators_page (ia);
634 /* Initializes IA's separators substructure. */
636 separators_page_create (PsppireImportAssistant *ia)
638 GtkBuilder *builder = ia->text_builder;
642 GtkWidget *w = get_widget_assert (builder, "Separators");
644 g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
645 g_object_set_data (G_OBJECT (w), "on-reset", reset_separators_page);
647 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
649 ia->custom_cb = get_widget_assert (builder, "custom-cb");
650 ia->custom_entry = get_widget_assert (builder, "custom-entry");
651 ia->quote_combo = get_widget_assert (builder, "quote-combo");
652 ia->quote_cb = get_widget_assert (builder, "quote-cb");
654 gtk_widget_set_sensitive (ia->custom_entry,
655 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->custom_cb)));
657 gtk_combo_box_set_active (GTK_COMBO_BOX (ia->quote_combo), 0);
659 if (ia->fields_tree_view == NULL)
661 GtkWidget *scroller = get_widget_assert (ia->text_builder, "fields-scroller");
662 ia->fields_tree_view = gtk_tree_view_new ();
663 g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL);
664 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
665 gtk_widget_show_all (scroller);
668 g_signal_connect (ia->quote_combo, "changed",
669 G_CALLBACK (on_quote_combo_change), ia);
670 g_signal_connect (ia->quote_cb, "toggled",
671 G_CALLBACK (on_quote_cb_toggle), ia);
672 g_signal_connect (ia->custom_entry, "notify::text",
673 G_CALLBACK (on_separators_custom_entry_notify), ia);
674 g_signal_connect (ia->custom_cb, "toggled",
675 G_CALLBACK (on_separators_custom_cb_toggle), ia);
676 for (i = 0; i < SEPARATOR_CNT; i++)
677 g_signal_connect (get_widget_assert (builder, separators[i].name),
678 "toggled", G_CALLBACK (on_separator_toggle), ia);
680 reset_separators_page (ia);
685 static struct casereader_random_class my_casereader_class;
687 static struct ccase *
688 my_read (struct casereader *reader, void *aux, casenumber idx)
690 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
691 GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
693 GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
695 const struct caseproto *proto = casereader_get_proto (reader);
698 struct ccase *c = NULL;
699 if (gtk_tree_model_get_iter (tm, &iter, tp))
701 c = case_create (proto);
703 for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
706 gtk_tree_model_get_value (tm, &iter, i + 1, &value);
708 const struct variable *var = dict_get_var (ia->casereader_dict, i);
710 const gchar *ss = g_value_get_string (&value);
713 union value *v = case_data_rw (c, var);
714 /* In this reader we derive the union value from the
715 string in the tree_model. We retrieve the width and format
716 from a dictionary which is stored directly after
717 the reader creation. Changes in ia->dict in the
718 variable window are not reflected here and therefore
719 this is always compatible with the width in the
720 caseproto. See bug #58298 */
721 char *xx = data_in (ss_cstr (ss),
723 var_get_write_format (var)->type,
724 settings_get_fmt_settings (),
725 v, var_get_width (var), "UTF-8");
729 g_value_unset (&value);
733 gtk_tree_path_free (tp);
739 my_destroy (struct casereader *reader, void *aux)
741 g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
745 my_advance (struct casereader *reader, void *aux, casenumber cnt)
747 g_print ("%s:%d\n", __FILE__, __LINE__);
750 static struct casereader *
751 textfile_create_reader (PsppireImportAssistant *ia)
753 int n_vars = dict_get_n_vars (ia->dict);
757 struct fmt_guesser **fg = XCALLOC (n_vars, struct fmt_guesser *);
758 for (i = 0 ; i < n_vars; ++i)
760 fg[i] = fmt_guesser_create ();
763 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
767 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter);
769 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter))
771 for (i = 0 ; i < n_vars; ++i)
774 gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1);
776 fmt_guesser_add (fg[i], ss_cstr (s));
781 struct caseproto *proto = caseproto_create ();
782 for (i = 0 ; i < n_vars; ++i)
785 fmt_guesser_guess (fg[i], &fs);
787 fmt_fix (&fs, FMT_FOR_INPUT);
789 struct variable *var = dict_get_var (ia->dict, i);
791 int width = fmt_var_width (&fs);
793 var_set_width_and_formats (var, width,
796 proto = caseproto_add_width (proto, width);
797 fmt_guesser_destroy (fg[i]);
802 struct casereader *cr = casereader_create_random (proto, n_rows, &my_casereader_class, ia);
803 /* Store the dictionary at this point when the casereader is created.
804 my_read depends on the dictionary to interpret the strings in the treeview.
805 This guarantees that the union value is produced according to the
806 caseproto in the reader. */
807 ia->casereader_dict = dict_clone (ia->dict);
808 caseproto_unref (proto);
813 /* When during import the variable type is changed, the reader is reinitialized
814 based on the new dictionary with a fresh caseprototype. The default behaviour
815 when a variable type is changed and the column is resized is that the union
816 value is interpreted with new variable type and an overlay for that column
817 is generated. Here we reinit to the original reader based on strings.
818 As a result you can switch from string to numeric to string without loosing
819 the string information. */
821 ia_variable_changed_cb (GObject *obj, gint var_num, guint what,
822 const struct variable *oldvar, gpointer data)
824 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (data);
826 struct caseproto *proto = caseproto_create();
827 for (int i = 0; i < dict_get_n_vars (ia->dict); i++)
829 const struct variable *var = dict_get_var (ia->dict, i);
830 int width = var_get_width (var);
831 proto = caseproto_add_width (proto, width);
834 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
836 PsppireDataStore *store = NULL;
837 g_object_get (ia->data_sheet, "data-model", &store, NULL);
839 struct casereader *cr = casereader_create_random (proto, n_rows,
840 &my_casereader_class, ia);
841 psppire_data_store_set_reader (store, cr);
842 dict_unref (ia->casereader_dict);
843 ia->casereader_dict = dict_clone (ia->dict);
847 /* Set the data model for both the data sheet and the variable sheet. */
849 textfile_set_data_models (PsppireImportAssistant *ia)
851 my_casereader_class.read = my_read;
852 my_casereader_class.destroy = my_destroy;
853 my_casereader_class.advance = my_advance;
855 struct casereader *reader = textfile_create_reader (ia);
857 PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
858 PsppireDataStore *store = psppire_data_store_new (dict);
859 psppire_data_store_set_reader (store, reader);
860 g_signal_connect (dict, "variable-changed",
861 G_CALLBACK (ia_variable_changed_cb),
864 g_object_set (ia->data_sheet, "data-model", store, NULL);
865 g_object_set (ia->var_sheet, "data-model", dict, NULL);
869 first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s)
872 g_object_get (ia->delimiters_model, "first-line", &first_case, NULL);
875 ds_put_format (s, " /FIRSTCASE=%d\n", first_case + 1);
878 /* Emits PSPP syntax to S that applies the dictionary attributes
879 (such as missing values and value labels) of the variables in
882 apply_dict (const struct dictionary *dict, struct string *s)
884 size_t n_vars = dict_get_n_vars (dict);
886 for (size_t i = 0; i < n_vars; i++)
888 struct variable *var = dict_get_var (dict, i);
889 const char *name = var_get_name (var);
890 enum val_type type = var_get_type (var);
891 int width = var_get_width (var);
892 enum measure measure = var_get_measure (var);
893 enum var_role role = var_get_role (var);
894 enum alignment alignment = var_get_alignment (var);
895 const struct fmt_spec *format = var_get_print_format (var);
897 if (var_has_missing_values (var))
899 const struct missing_values *mv = var_get_missing_values (var);
902 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
903 for (j = 0; j < mv_n_values (mv); j++)
906 ds_put_cstr (s, ", ");
907 syntax_gen_value (s, mv_get_value (mv, j), width, format);
910 if (mv_has_range (mv))
913 if (mv_has_value (mv))
914 ds_put_cstr (s, ", ");
915 mv_get_range (mv, &low, &high);
916 syntax_gen_num_range (s, low, high, format);
918 ds_put_cstr (s, ").\n");
920 if (var_has_value_labels (var))
922 const struct val_labs *vls = var_get_value_labels (var);
923 const struct val_lab **labels = val_labs_sorted (vls);
924 size_t n_labels = val_labs_count (vls);
926 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
927 for (size_t j = 0; j < n_labels; j++)
929 const struct val_lab *vl = labels[j];
930 ds_put_cstr (s, "\n ");
931 syntax_gen_value (s, &vl->value, width, format);
932 ds_put_byte (s, ' ');
933 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
936 ds_put_cstr (s, ".\n");
938 if (var_has_label (var))
939 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
940 name, var_get_label (var));
941 if (measure != var_default_measure (type))
942 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
943 name, measure_to_syntax (measure));
944 if (role != ROLE_INPUT)
945 syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
946 var_role_to_syntax (role), name);
947 if (alignment != var_default_alignment (type))
948 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
949 name, alignment_to_syntax (alignment));
950 if (var_get_display_width (var) != var_default_display_width (width))
951 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
952 name, var_get_display_width (var));
958 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
961 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
963 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
964 ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n",
965 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line);
966 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
967 ds_put_format (s, "SAMPLE %.4g.\n",
968 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
973 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
975 g_return_if_fail (ia->dict);
977 ds_put_cstr (s, " /VARIABLES=\n");
979 int n_vars = dict_get_n_vars (ia->dict);
980 for (int i = 0; i < n_vars; i++)
982 struct variable *var = dict_get_var (ia->dict, i);
983 char format_string[FMT_STRING_LEN_MAX + 1];
984 fmt_to_string (var_get_print_format (var), format_string);
985 ds_put_format (s, " %s %s%s\n",
986 var_get_name (var), format_string,
987 i == n_vars - 1 ? "." : "");
992 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
996 ds_put_cstr (s, " /DELIMITERS=\"");
998 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->text_builder, "tab"))))
999 ds_put_cstr (s, "\\t");
1000 for (i = 0; i < SEPARATOR_CNT; i++)
1002 const struct separator *seps = &separators[i];
1003 GtkWidget *button = get_widget_assert (ia->text_builder, seps->name);
1004 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1006 if (seps->c == '\t')
1009 ds_put_byte (s, seps->c);
1012 ds_put_cstr (s, "\"\n");
1014 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb)))
1016 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
1017 gchar *quotes = gtk_combo_box_text_get_active_text (cbt);
1018 if (quotes && *quotes)
1019 syntax_gen_pspp (s, " /QUALIFIER=%sq\n", quotes);
1026 text_spec_gen_syntax (PsppireImportAssistant *ia, struct string *s)
1028 gchar *file_name = NULL;
1029 gchar *encoding = NULL;
1030 g_object_get (ia->text_file,
1031 "file-name", &file_name,
1032 "encoding", &encoding,
1035 if (file_name == NULL)
1043 if (encoding && strcmp (encoding, "Auto"))
1044 syntax_gen_pspp (s, " /ENCODING=%sq\n", encoding);
1047 " /ARRANGEMENT=DELIMITED\n"
1048 " /DELCASE=LINE\n");
1050 first_line_append_syntax (ia, s);
1051 separators_append_syntax (ia, s);
1053 formats_append_syntax (ia, s);
1054 apply_dict (ia->dict, s);
1055 intro_append_syntax (ia, s);