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);
79 struct hmap count_map[SEPARATOR_CNT];
80 for (j = 0; j < SEPARATOR_CNT; ++j)
81 hmap_init (count_map + j);
83 GtkTreePath *p = gtk_tree_path_new_from_indices (first_line, -1);
85 for (valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (ia->text_file), &iter, p);
87 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->text_file), &iter))
89 gchar *line_text = NULL;
90 gtk_tree_model_get (GTK_TREE_MODEL (ia->text_file), &iter, 1, &line_text, -1);
92 gint *counts = xzalloc (sizeof *counts * SEPARATOR_CNT);
94 struct substring cs = ss_cstr (line_text);
96 UINT32_MAX != ss_first_mb (cs);
99 ucs4_t character = ss_first_mb (cs);
102 for (s = 0; s < SEPARATOR_CNT; ++s)
104 if (character == separators[s].c)
110 for (j = 0; j < SEPARATOR_CNT; ++j)
114 struct separator_count_node *cn = NULL;
115 unsigned int hash = hash_int (counts[j], 0);
116 HMAP_FOR_EACH_WITH_HASH (cn, struct separator_count_node, node, hash, &count_map[j])
118 if (cn->occurance == counts[j])
124 struct separator_count_node *new_cn = xzalloc (sizeof *new_cn);
125 new_cn->occurance = counts[j];
126 new_cn->quantity = 1;
127 hmap_insert (&count_map[j], &new_cn->node, hash);
137 gtk_tree_path_free (p);
139 if (hmap_count (count_map) > 0)
141 int most_frequent = -1;
143 for (j = 0; j < SEPARATOR_CNT; ++j)
145 struct separator_count_node *cn;
146 struct separator_count_node *next;
147 HMAP_FOR_EACH_SAFE (cn, next, struct separator_count_node, node, &count_map[j])
149 if (largest < cn->quantity)
151 largest = cn->quantity;
156 hmap_destroy (&count_map[j]);
159 g_return_if_fail (most_frequent >= 0);
162 get_widget_assert (ia->text_builder, separators[most_frequent].name);
163 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
168 repopulate_delimiter_columns (PsppireImportAssistant *ia)
170 /* Remove all the columns */
171 while (gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view)) > 0)
173 GtkTreeViewColumn *tvc = gtk_tree_view_get_column (GTK_TREE_VIEW (ia->fields_tree_view), 0);
174 gtk_tree_view_remove_column (GTK_TREE_VIEW (ia->fields_tree_view), tvc);
178 gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model));
180 /* ... and put them back again. */
182 for (f = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view));
185 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
187 const gchar *title = NULL;
193 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
196 psppire_delimited_text_get_header_title
197 (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), f - 1);
203 GtkTreeViewColumn *column =
204 gtk_tree_view_column_new_with_attributes (title,
208 g_object_set (column,
210 "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
213 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->fields_tree_view), column);
218 reset_tree_view_model (PsppireImportAssistant *ia)
220 GtkTreeModel *tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ia->fields_tree_view));
222 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), NULL);
225 repopulate_delimiter_columns (ia);
227 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), tm);
228 // gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ia->fields_tree_view));
233 /* Resets IA's intro page to its initial state. */
235 reset_intro_page (PsppireImportAssistant *ia)
237 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->n_cases_button),
239 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->percent_button),
241 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->all_cases_button),
244 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ia->n_cases_spin), 1);
245 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ia->percent_spin), 0);
248 /* Called when one of the radio buttons is clicked. */
250 on_intro_amount_changed (PsppireImportAssistant *ia)
252 gtk_widget_set_sensitive (ia->n_cases_spin,
253 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)));
255 gtk_widget_set_sensitive (ia->percent_spin,
256 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)));
260 on_treeview_selection_change (PsppireImportAssistant *ia)
262 GtkTreeSelection *selection =
263 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
264 GtkTreeModel *model = NULL;
266 if (gtk_tree_selection_get_selected (selection, &model, &iter))
270 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
271 gint *index = gtk_tree_path_get_indices (path);
273 gtk_tree_path_free (path);
274 g_object_get (model, "maximum-lines", &max_lines, NULL);
275 gtk_widget_set_sensitive (ia->variable_names_cb,
276 (n > 0 && n < max_lines));
277 ia->delimiters_model =
278 psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file));
279 g_object_set (ia->delimiters_model, "first-line", n, NULL);
284 render_text_preview_line (GtkTreeViewColumn *tree_column,
285 GtkCellRenderer *cell,
286 GtkTreeModel *tree_model,
291 Set the text to a "insensitive" state if the row
292 is greater than what the user declared to be the maximum.
294 GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
295 gint *ii = gtk_tree_path_get_indices (path);
297 g_object_get (tree_model, "maximum-lines", &max_lines, NULL);
298 g_object_set (cell, "sensitive", (*ii < max_lines), NULL);
299 gtk_tree_path_free (path);
302 /* Resets IA's "first line" page to its initial state. */
304 reset_first_line_page (PsppireImportAssistant *ia)
306 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb), FALSE);
308 GtkTreeSelection *selection =
309 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
311 gtk_tree_selection_unselect_all (selection);
314 /* Initializes IA's first_line substructure. */
316 first_line_page_create (PsppireImportAssistant *ia)
318 GtkWidget *w = get_widget_assert (ia->text_builder, "FirstLine");
320 g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change);
321 g_object_set_data (G_OBJECT (w), "on-reset", reset_first_line_page);
323 add_page_to_assistant (ia, w,
324 GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line"));
326 GtkWidget *scrolled_window = get_widget_assert (ia->text_builder, "first-line-scroller");
328 if (ia->first_line_tree_view == NULL)
330 ia->first_line_tree_view = gtk_tree_view_new ();
331 g_object_set (ia->first_line_tree_view, "enable-search", FALSE, NULL);
333 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE);
335 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
336 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer,
340 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
341 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
343 renderer = gtk_cell_renderer_text_new ();
344 column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL);
345 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
347 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
349 g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed",
350 G_CALLBACK (on_treeview_selection_change), ia);
351 gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view);
354 gtk_widget_show_all (scrolled_window);
356 ia->variable_names_cb = get_widget_assert (ia->text_builder, "variable-names");
358 reset_first_line_page (ia);
362 intro_on_leave (PsppireImportAssistant *ia, GtkWidget *page, enum IMPORT_ASSISTANT_DIRECTION dir)
364 if (dir != IMPORT_ASSISTANT_FORWARDS)
368 g_object_get (ia->text_file, "line-count", &lc, NULL);
369 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
371 gint max_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin));
372 g_object_set (ia->text_file, "maximum-lines", max_lines, NULL);
374 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
376 gdouble percent = gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin));
377 g_object_set (ia->text_file, "maximum-lines", (gint) (lc * percent / 100.0), NULL);
381 g_object_set (ia->text_file, "maximum-lines", lc, NULL);
387 intro_on_enter (PsppireImportAssistant *ia, GtkWidget *page, enum IMPORT_ASSISTANT_DIRECTION dir)
389 GtkBuilder *builder = ia->text_builder;
390 GtkWidget *table = get_widget_assert (builder, "button-table");
395 ds_put_cstr (&s, _("This assistant will guide you through the process of "
396 "importing data into PSPP from a text file with one line "
397 "per case, in which fields are separated by tabs, "
398 "commas, or other delimiters.\n\n"));
402 if (ia->text_file->total_is_exact)
405 &s, ngettext ("The selected file contains %'lu line of text. ",
406 "The selected file contains %'lu lines of text. ",
407 ia->text_file->total_lines),
408 ia->text_file->total_lines);
410 else if (ia->text_file->total_lines > 0)
414 "The selected file contains approximately %'lu line of text. ",
415 "The selected file contains approximately %'lu lines of text. ",
416 ia->text_file->total_lines),
417 ia->text_file->total_lines);
420 "Only the first %zu line of the file will be shown for "
421 "preview purposes in the following screens. ",
422 "Only the first %zu lines of the file will be shown for "
423 "preview purposes in the following screens. ",
424 ia->text_file->line_cnt),
425 ia->text_file->line_cnt);
429 ds_put_cstr (&s, _("You may choose below how much of the file should "
430 "actually be imported."));
432 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
436 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 1) == NULL)
438 GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin);
439 gtk_grid_attach (GTK_GRID (table), hbox_n_cases,
444 GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin));
445 gtk_adjustment_set_lower (adj, 1.0);
447 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 2) == NULL)
449 GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"),
452 gtk_grid_attach (GTK_GRID (table), hbox_percent,
457 gtk_widget_show_all (table);
460 if (dir != IMPORT_ASSISTANT_FORWARDS)
463 reset_intro_page (ia);
464 on_intro_amount_changed (ia);
467 /* Initializes IA's intro substructure. */
469 intro_page_create (PsppireImportAssistant *ia)
471 GtkBuilder *builder = ia->text_builder;
473 GtkWidget *w = get_widget_assert (builder, "Intro");
475 ia->percent_spin = gtk_spin_button_new_with_range (0, 100, 1);
477 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Select the Lines to Import"));
479 ia->all_cases_button = get_widget_assert (builder, "import-all-cases");
480 ia->n_cases_button = get_widget_assert (builder, "import-n-cases");
481 ia->percent_button = get_widget_assert (builder, "import-percent");
483 g_signal_connect_swapped (ia->all_cases_button, "toggled",
484 G_CALLBACK (on_intro_amount_changed), ia);
485 g_signal_connect_swapped (ia->n_cases_button, "toggled",
486 G_CALLBACK (on_intro_amount_changed), ia);
487 g_signal_connect_swapped (ia->percent_button, "toggled",
488 G_CALLBACK (on_intro_amount_changed), ia);
490 g_object_set_data (G_OBJECT (w), "on-leaving", intro_on_leave);
491 g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter);
492 g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page);
498 /* Chooses a name for each column on the separators page */
500 choose_column_names (PsppireImportAssistant *ia)
503 unsigned long int generated_name_count = 0;
504 char *encoding = NULL;
505 g_object_get (ia->text_file, "encoding", &encoding, NULL);
507 dict_unref (ia->dict);
508 ia->dict = dict_create (encoding ? encoding : UTF8);
512 i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)) - 1;
515 const gchar *candidate_name = NULL;
517 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
519 candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i);
522 char *name = dict_make_unique_var_name (ia->dict,
524 &generated_name_count);
526 dict_create_var_assert (ia->dict, name, 0);
531 /* Called when the user toggles one of the separators
534 on_separator_toggle (GtkToggleButton *toggle UNUSED,
535 PsppireImportAssistant *ia)
538 GSList *delimiters = NULL;
539 for (i = 0; i < SEPARATOR_CNT; i++)
541 const struct separator *s = &separators[i];
542 GtkWidget *button = get_widget_assert (ia->text_builder, s->name);
543 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
545 delimiters = g_slist_prepend (delimiters, GINT_TO_POINTER (s->c));
549 g_object_set (ia->delimiters_model, "delimiters", delimiters, NULL);
551 revise_fields_preview (ia);
555 /* Called when the user changes the entry field for custom
558 on_separators_custom_entry_notify (GObject *gobject UNUSED,
559 GParamSpec *arg1 UNUSED,
560 PsppireImportAssistant *ia)
562 revise_fields_preview (ia);
565 /* Called when the user toggles the checkbox that enables custom
568 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
569 PsppireImportAssistant *ia)
571 bool is_active = gtk_toggle_button_get_active (custom_cb);
572 gtk_widget_set_sensitive (ia->custom_entry, is_active);
573 revise_fields_preview (ia);
576 /* Called when the user changes the selection in the combo box
577 that selects a quote character. */
579 on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia)
581 revise_fields_preview (ia);
584 /* Called when the user toggles the checkbox that enables
587 on_quote_cb_toggle (GtkToggleButton *quote_cb, PsppireImportAssistant *ia)
589 bool is_active = gtk_toggle_button_get_active (quote_cb);
590 gtk_widget_set_sensitive (ia->quote_combo, is_active);
591 revise_fields_preview (ia);
595 /* Called when the Reset button is clicked. */
597 reset_separators_page (PsppireImportAssistant *ia)
599 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->custom_cb), FALSE);
600 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->quote_cb), FALSE);
601 gtk_entry_set_text (GTK_ENTRY (ia->custom_entry), "");
603 for (gint i = 0; i < SEPARATOR_CNT; i++)
605 const struct separator *s = &separators[i];
606 GtkWidget *button = get_widget_assert (ia->text_builder, s->name);
607 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE);
610 repopulate_delimiter_columns (ia);
612 revise_fields_preview (ia);
613 choose_likely_separators (ia);
616 /* Called just before the separators page becomes visible in the
619 prepare_separators_page (PsppireImportAssistant *ia, GtkWidget *new_page, enum IMPORT_ASSISTANT_DIRECTION dir)
621 if (dir != IMPORT_ASSISTANT_FORWARDS)
624 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view),
625 GTK_TREE_MODEL (ia->delimiters_model));
627 g_signal_connect_swapped (GTK_TREE_MODEL (ia->delimiters_model), "notify::delimiters",
628 G_CALLBACK (reset_tree_view_model), ia);
631 reset_separators_page (ia);
635 /* Initializes IA's separators substructure. */
637 separators_page_create (PsppireImportAssistant *ia)
639 GtkBuilder *builder = ia->text_builder;
643 GtkWidget *w = get_widget_assert (builder, "Separators");
645 g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
646 g_object_set_data (G_OBJECT (w), "on-reset", reset_separators_page);
648 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
650 ia->custom_cb = get_widget_assert (builder, "custom-cb");
651 ia->custom_entry = get_widget_assert (builder, "custom-entry");
652 ia->quote_combo = get_widget_assert (builder, "quote-combo");
653 ia->quote_cb = get_widget_assert (builder, "quote-cb");
655 gtk_widget_set_sensitive (ia->custom_entry,
656 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->custom_cb)));
658 gtk_combo_box_set_active (GTK_COMBO_BOX (ia->quote_combo), 0);
660 if (ia->fields_tree_view == NULL)
662 GtkWidget *scroller = get_widget_assert (ia->text_builder, "fields-scroller");
663 ia->fields_tree_view = gtk_tree_view_new ();
664 g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL);
665 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
666 gtk_widget_show_all (scroller);
669 g_signal_connect (ia->quote_combo, "changed",
670 G_CALLBACK (on_quote_combo_change), ia);
671 g_signal_connect (ia->quote_cb, "toggled",
672 G_CALLBACK (on_quote_cb_toggle), ia);
673 g_signal_connect (ia->custom_entry, "notify::text",
674 G_CALLBACK (on_separators_custom_entry_notify), ia);
675 g_signal_connect (ia->custom_cb, "toggled",
676 G_CALLBACK (on_separators_custom_cb_toggle), ia);
677 for (i = 0; i < SEPARATOR_CNT; i++)
678 g_signal_connect (get_widget_assert (builder, separators[i].name),
679 "toggled", G_CALLBACK (on_separator_toggle), ia);
681 reset_separators_page (ia);
686 static struct casereader_random_class my_casereader_class;
688 static struct ccase *
689 my_read (struct casereader *reader, void *aux, casenumber idx)
691 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
692 GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
694 GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
696 const struct caseproto *proto = casereader_get_proto (reader);
699 struct ccase *c = NULL;
700 if (gtk_tree_model_get_iter (tm, &iter, tp))
702 c = case_create (proto);
704 for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
707 gtk_tree_model_get_value (tm, &iter, i + 1, &value);
709 const struct variable *var = dict_get_var (ia->casereader_dict, i);
711 const gchar *ss = g_value_get_string (&value);
714 union value *v = case_data_rw (c, var);
715 /* In this reader we derive the union value from the
716 string in the tree_model. We retrieve the width and format
717 from a dictionary which is stored directly after
718 the reader creation. Changes in ia->dict in the
719 variable window are not reflected here and therefore
720 this is always compatible with the width in the
721 caseproto. See bug #58298 */
722 char *xx = data_in (ss_cstr (ss),
724 var_get_write_format (var)->type,
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_var_cnt (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_var_cnt (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 var_cnt = dict_get_var_cnt (dict);
887 for (i = 0; i < var_cnt; i++)
889 struct variable *var = dict_get_var (dict, i);
890 const char *name = var_get_name (var);
891 enum val_type type = var_get_type (var);
892 int width = var_get_width (var);
893 enum measure measure = var_get_measure (var);
894 enum var_role role = var_get_role (var);
895 enum alignment alignment = var_get_alignment (var);
896 const struct fmt_spec *format = var_get_print_format (var);
898 if (var_has_missing_values (var))
900 const struct missing_values *mv = var_get_missing_values (var);
903 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
904 for (j = 0; j < mv_n_values (mv); j++)
907 ds_put_cstr (s, ", ");
908 syntax_gen_value (s, mv_get_value (mv, j), width, format);
911 if (mv_has_range (mv))
914 if (mv_has_value (mv))
915 ds_put_cstr (s, ", ");
916 mv_get_range (mv, &low, &high);
917 syntax_gen_num_range (s, low, high, format);
919 ds_put_cstr (s, ").\n");
921 if (var_has_value_labels (var))
923 const struct val_labs *vls = var_get_value_labels (var);
924 const struct val_lab **labels = val_labs_sorted (vls);
925 size_t n_labels = val_labs_count (vls);
928 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
929 for (i = 0; i < n_labels; i++)
931 const struct val_lab *vl = labels[i];
932 ds_put_cstr (s, "\n ");
933 syntax_gen_value (s, &vl->value, width, format);
934 ds_put_byte (s, ' ');
935 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
938 ds_put_cstr (s, ".\n");
940 if (var_has_label (var))
941 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
942 name, var_get_label (var));
943 if (measure != var_default_measure (type))
944 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
945 name, measure_to_syntax (measure));
946 if (role != ROLE_INPUT)
947 syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
948 var_role_to_syntax (role), name);
949 if (alignment != var_default_alignment (type))
950 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
951 name, alignment_to_syntax (alignment));
952 if (var_get_display_width (var) != var_default_display_width (width))
953 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
954 name, var_get_display_width (var));
960 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
963 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
965 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
966 ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n",
967 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line);
968 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
969 ds_put_format (s, "SAMPLE %.4g.\n",
970 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
975 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
980 g_return_if_fail (ia->dict);
982 ds_put_cstr (s, " /VARIABLES=\n");
984 var_cnt = dict_get_var_cnt (ia->dict);
985 for (i = 0; i < var_cnt; i++)
987 struct variable *var = dict_get_var (ia->dict, i);
988 char format_string[FMT_STRING_LEN_MAX + 1];
989 fmt_to_string (var_get_print_format (var), format_string);
990 ds_put_format (s, " %s %s%s\n",
991 var_get_name (var), format_string,
992 i == var_cnt - 1 ? "." : "");
997 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1001 ds_put_cstr (s, " /DELIMITERS=\"");
1003 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->text_builder, "tab"))))
1004 ds_put_cstr (s, "\\t");
1005 for (i = 0; i < SEPARATOR_CNT; i++)
1007 const struct separator *seps = &separators[i];
1008 GtkWidget *button = get_widget_assert (ia->text_builder, seps->name);
1009 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1011 if (seps->c == '\t')
1014 ds_put_byte (s, seps->c);
1017 ds_put_cstr (s, "\"\n");
1019 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb)))
1021 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
1022 gchar *quotes = gtk_combo_box_text_get_active_text (cbt);
1023 if (quotes && *quotes)
1024 syntax_gen_pspp (s, " /QUALIFIER=%sq\n", quotes);
1031 text_spec_gen_syntax (PsppireImportAssistant *ia, struct string *s)
1033 gchar *file_name = NULL;
1034 gchar *encoding = NULL;
1035 g_object_get (ia->text_file,
1036 "file-name", &file_name,
1037 "encoding", &encoding,
1040 if (file_name == NULL)
1048 if (encoding && strcmp (encoding, "Auto"))
1049 syntax_gen_pspp (s, " /ENCODING=%sq\n", encoding);
1052 " /ARRANGEMENT=DELIMITED\n"
1053 " /DELCASE=LINE\n");
1055 first_line_append_syntax (ia, s);
1056 separators_append_syntax (ia, s);
1058 formats_append_syntax (ia, s);
1059 apply_dict (ia->dict, s);
1060 intro_append_syntax (ia, s);