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 = 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 (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->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, 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 v, var_get_width (var), "UTF-8");
728 g_value_unset (&value);
732 gtk_tree_path_free (tp);
738 my_destroy (struct casereader *reader, void *aux)
740 g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
744 my_advance (struct casereader *reader, void *aux, casenumber cnt)
746 g_print ("%s:%d\n", __FILE__, __LINE__);
749 static struct casereader *
750 textfile_create_reader (PsppireImportAssistant *ia)
752 int n_vars = dict_get_var_cnt (ia->dict);
756 struct fmt_guesser **fg = XCALLOC (n_vars, struct fmt_guesser *);
757 for (i = 0 ; i < n_vars; ++i)
759 fg[i] = fmt_guesser_create ();
762 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
766 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter);
768 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter))
770 for (i = 0 ; i < n_vars; ++i)
773 gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1);
775 fmt_guesser_add (fg[i], ss_cstr (s));
780 struct caseproto *proto = caseproto_create ();
781 for (i = 0 ; i < n_vars; ++i)
784 fmt_guesser_guess (fg[i], &fs);
786 fmt_fix (&fs, FMT_FOR_INPUT);
788 struct variable *var = dict_get_var (ia->dict, i);
790 int width = fmt_var_width (&fs);
792 var_set_width_and_formats (var, width,
795 proto = caseproto_add_width (proto, width);
796 fmt_guesser_destroy (fg[i]);
801 struct casereader *cr = casereader_create_random (proto, n_rows, &my_casereader_class, ia);
802 /* Store the dictionary at this point when the casereader is created.
803 my_read depends on the dictionary to interpret the strings in the treeview.
804 This guarantees that the union value is produced according to the
805 caseproto in the reader. */
806 ia->casereader_dict = dict_clone (ia->dict);
807 caseproto_unref (proto);
812 /* When during import the variable type is changed, the reader is reinitialized
813 based on the new dictionary with a fresh caseprototype. The default behaviour
814 when a variable type is changed and the column is resized is that the union
815 value is interpreted with new variable type and an overlay for that column
816 is generated. Here we reinit to the original reader based on strings.
817 As a result you can switch from string to numeric to string without loosing
818 the string information. */
820 ia_variable_changed_cb (GObject *obj, gint var_num, guint what,
821 const struct variable *oldvar, gpointer data)
823 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (data);
825 struct caseproto *proto = caseproto_create();
826 for (int i = 0; i < dict_get_var_cnt (ia->dict); i++)
828 const struct variable *var = dict_get_var (ia->dict, i);
829 int width = var_get_width (var);
830 proto = caseproto_add_width (proto, width);
833 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
835 PsppireDataStore *store = NULL;
836 g_object_get (ia->data_sheet, "data-model", &store, NULL);
838 struct casereader *cr = casereader_create_random (proto, n_rows,
839 &my_casereader_class, ia);
840 psppire_data_store_set_reader (store, cr);
841 dict_unref (ia->casereader_dict);
842 ia->casereader_dict = dict_clone (ia->dict);
846 /* Set the data model for both the data sheet and the variable sheet. */
848 textfile_set_data_models (PsppireImportAssistant *ia)
850 my_casereader_class.read = my_read;
851 my_casereader_class.destroy = my_destroy;
852 my_casereader_class.advance = my_advance;
854 struct casereader *reader = textfile_create_reader (ia);
856 PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
857 PsppireDataStore *store = psppire_data_store_new (dict);
858 psppire_data_store_set_reader (store, reader);
859 g_signal_connect (dict, "variable-changed",
860 G_CALLBACK (ia_variable_changed_cb),
863 g_object_set (ia->data_sheet, "data-model", store, NULL);
864 g_object_set (ia->var_sheet, "data-model", dict, NULL);
868 first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s)
871 g_object_get (ia->delimiters_model, "first-line", &first_case, NULL);
874 ds_put_format (s, " /FIRSTCASE=%d\n", first_case + 1);
877 /* Emits PSPP syntax to S that applies the dictionary attributes
878 (such as missing values and value labels) of the variables in
881 apply_dict (const struct dictionary *dict, struct string *s)
883 size_t var_cnt = dict_get_var_cnt (dict);
885 for (size_t i = 0; i < var_cnt; i++)
887 struct variable *var = dict_get_var (dict, i);
888 const char *name = var_get_name (var);
889 enum val_type type = var_get_type (var);
890 int width = var_get_width (var);
891 enum measure measure = var_get_measure (var);
892 enum var_role role = var_get_role (var);
893 enum alignment alignment = var_get_alignment (var);
894 const struct fmt_spec *format = var_get_print_format (var);
896 if (var_has_missing_values (var))
898 const struct missing_values *mv = var_get_missing_values (var);
901 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
902 for (j = 0; j < mv_n_values (mv); j++)
905 ds_put_cstr (s, ", ");
906 syntax_gen_value (s, mv_get_value (mv, j), width, format);
909 if (mv_has_range (mv))
912 if (mv_has_value (mv))
913 ds_put_cstr (s, ", ");
914 mv_get_range (mv, &low, &high);
915 syntax_gen_num_range (s, low, high, format);
917 ds_put_cstr (s, ").\n");
919 if (var_has_value_labels (var))
921 const struct val_labs *vls = var_get_value_labels (var);
922 const struct val_lab **labels = val_labs_sorted (vls);
923 size_t n_labels = val_labs_count (vls);
925 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
926 for (size_t j = 0; j < n_labels; j++)
928 const struct val_lab *vl = labels[j];
929 ds_put_cstr (s, "\n ");
930 syntax_gen_value (s, &vl->value, width, format);
931 ds_put_byte (s, ' ');
932 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
935 ds_put_cstr (s, ".\n");
937 if (var_has_label (var))
938 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
939 name, var_get_label (var));
940 if (measure != var_default_measure (type))
941 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
942 name, measure_to_syntax (measure));
943 if (role != ROLE_INPUT)
944 syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
945 var_role_to_syntax (role), name);
946 if (alignment != var_default_alignment (type))
947 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
948 name, alignment_to_syntax (alignment));
949 if (var_get_display_width (var) != var_default_display_width (width))
950 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
951 name, var_get_display_width (var));
957 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
960 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
962 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
963 ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n",
964 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line);
965 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
966 ds_put_format (s, "SAMPLE %.4g.\n",
967 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
972 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
977 g_return_if_fail (ia->dict);
979 ds_put_cstr (s, " /VARIABLES=\n");
981 var_cnt = dict_get_var_cnt (ia->dict);
982 for (i = 0; i < var_cnt; i++)
984 struct variable *var = dict_get_var (ia->dict, i);
985 char format_string[FMT_STRING_LEN_MAX + 1];
986 fmt_to_string (var_get_print_format (var), format_string);
987 ds_put_format (s, " %s %s%s\n",
988 var_get_name (var), format_string,
989 i == var_cnt - 1 ? "." : "");
994 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
998 ds_put_cstr (s, " /DELIMITERS=\"");
1000 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->text_builder, "tab"))))
1001 ds_put_cstr (s, "\\t");
1002 for (i = 0; i < SEPARATOR_CNT; i++)
1004 const struct separator *seps = &separators[i];
1005 GtkWidget *button = get_widget_assert (ia->text_builder, seps->name);
1006 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1008 if (seps->c == '\t')
1011 ds_put_byte (s, seps->c);
1014 ds_put_cstr (s, "\"\n");
1016 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb)))
1018 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
1019 gchar *quotes = gtk_combo_box_text_get_active_text (cbt);
1020 if (quotes && *quotes)
1021 syntax_gen_pspp (s, " /QUALIFIER=%sq\n", quotes);
1028 text_spec_gen_syntax (PsppireImportAssistant *ia, struct string *s)
1030 gchar *file_name = NULL;
1031 gchar *encoding = NULL;
1032 g_object_get (ia->text_file,
1033 "file-name", &file_name,
1034 "encoding", &encoding,
1037 if (file_name == NULL)
1045 if (encoding && strcmp (encoding, "Auto"))
1046 syntax_gen_pspp (s, " /ENCODING=%sq\n", encoding);
1049 " /ARRANGEMENT=DELIMITED\n"
1050 " /DELCASE=LINE\n");
1052 first_line_append_syntax (ia, s);
1053 separators_append_syntax (ia, s);
1055 formats_append_syntax (ia, s);
1056 apply_dict (ia->dict, s);
1057 intro_append_syntax (ia, s);