1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2015, 2016, 2017, 2018, 2020 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "psppire-import-textfile.h"
22 #include "libpspp/i18n.h"
23 #include "libpspp/line-reader.h"
24 #include "libpspp/message.h"
25 #include "libpspp/hmap.h"
26 #include "libpspp/hash-functions.h"
27 #include "libpspp/str.h"
28 #include "libpspp/misc.h"
30 #include "data/casereader.h"
31 #include "data/casereader-provider.h"
32 #include "data/data-in.h"
33 #include "data/format-guesser.h"
34 #include "data/value-labels.h"
36 #include "builder-wrapper.h"
38 #include "psppire-data-store.h"
39 #include "psppire-scanf.h"
41 #include "ui/syntax-gen.h"
44 #define _(msgid) gettext (msgid)
45 #define N_(msgid) msgid
47 /* Chooses a name for each column on the separators page */
48 static void choose_column_names (PsppireImportAssistant *ia);
50 /* Revises the contents of the fields tree view based on the
51 currently chosen set of separators and quotes. */
53 revise_fields_preview (PsppireImportAssistant *ia)
55 GSList *delimiters = NULL;
56 for (int i = 0; i < N_SEPARATORS; i++)
58 const struct separator *s = &separators[i];
59 GtkWidget *button = get_widget_assert (ia->text_builder, s->name);
60 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
62 delimiters = g_slist_prepend (delimiters, GINT_TO_POINTER (s->c));
66 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
67 GtkToggleButton *quote_cb = GTK_TOGGLE_BUTTON (ia->quote_cb);
68 const gchar *quotes = (gtk_toggle_button_get_active (quote_cb)
69 ? gtk_combo_box_text_get_active_text (cbt)
72 g_object_set (ia->delimiters_model,
73 "delimiters", delimiters,
77 choose_column_names (ia);
82 repopulate_delimiter_columns (PsppireImportAssistant *ia)
84 /* Remove all the columns */
85 while (gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view)) > 0)
87 GtkTreeViewColumn *tvc = gtk_tree_view_get_column (GTK_TREE_VIEW (ia->fields_tree_view), 0);
88 gtk_tree_view_remove_column (GTK_TREE_VIEW (ia->fields_tree_view), tvc);
92 gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model));
94 /* ... and put them back again. */
96 for (f = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view));
99 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
101 const gchar *title = NULL;
107 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
110 psppire_delimited_text_get_header_title
111 (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), f - 1);
117 GtkTreeViewColumn *column =
118 gtk_tree_view_column_new_with_attributes (title,
122 g_object_set (column,
124 "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
127 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->fields_tree_view), column);
132 reset_tree_view_model (PsppireImportAssistant *ia)
134 GtkTreeModel *tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ia->fields_tree_view));
136 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), NULL);
139 repopulate_delimiter_columns (ia);
141 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), tm);
142 // gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ia->fields_tree_view));
147 /* Resets IA's intro page to its initial state. */
149 reset_intro_page (PsppireImportAssistant *ia)
151 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->n_cases_button),
153 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->percent_button),
155 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->all_cases_button),
158 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ia->n_cases_spin), 1);
159 gtk_spin_button_set_value (GTK_SPIN_BUTTON (ia->percent_spin), 0);
162 /* Called when one of the radio buttons is clicked. */
164 on_intro_amount_changed (PsppireImportAssistant *ia)
166 gtk_widget_set_sensitive (ia->n_cases_spin,
167 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)));
169 gtk_widget_set_sensitive (ia->percent_spin,
170 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)));
174 on_treeview_selection_change (PsppireImportAssistant *ia)
176 GtkTreeSelection *selection =
177 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
178 GtkTreeModel *model = NULL;
180 if (gtk_tree_selection_get_selected (selection, &model, &iter))
184 GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
185 gint *index = gtk_tree_path_get_indices (path);
187 gtk_tree_path_free (path);
188 g_object_get (model, "maximum-lines", &max_lines, NULL);
189 gtk_widget_set_sensitive (ia->variable_names_cb,
190 (n > 0 && n < max_lines));
191 ia->delimiters_model =
192 psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file));
193 g_object_set (ia->delimiters_model, "first-line", n, NULL);
198 render_text_preview_line (GtkTreeViewColumn *tree_column,
199 GtkCellRenderer *cell,
200 GtkTreeModel *tree_model,
205 Set the text to a "insensitive" state if the row
206 is greater than what the user declared to be the maximum.
208 GtkTreePath *path = gtk_tree_model_get_path (tree_model, iter);
209 gint *ii = gtk_tree_path_get_indices (path);
211 g_object_get (tree_model, "maximum-lines", &max_lines, NULL);
212 g_object_set (cell, "sensitive", (*ii < max_lines), NULL);
213 gtk_tree_path_free (path);
216 /* Resets IA's "first line" page to its initial state. */
218 reset_first_line_page (PsppireImportAssistant *ia)
220 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb), FALSE);
222 GtkTreeSelection *selection =
223 gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
225 gtk_tree_selection_unselect_all (selection);
228 /* Initializes IA's first_line substructure. */
230 first_line_page_create (PsppireImportAssistant *ia)
232 GtkWidget *w = get_widget_assert (ia->text_builder, "FirstLine");
234 g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change);
235 g_object_set_data (G_OBJECT (w), "on-reset", reset_first_line_page);
237 add_page_to_assistant (ia, w,
238 GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line"));
240 GtkWidget *scrolled_window = get_widget_assert (ia->text_builder, "first-line-scroller");
242 if (ia->first_line_tree_view == NULL)
244 ia->first_line_tree_view = gtk_tree_view_new ();
245 g_object_set (ia->first_line_tree_view, "enable-search", FALSE, NULL);
247 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE);
249 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
250 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer,
254 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
255 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
257 renderer = gtk_cell_renderer_text_new ();
258 column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL);
259 gtk_tree_view_column_set_cell_data_func (column, renderer, render_text_preview_line, ia, 0);
261 gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
263 g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed",
264 G_CALLBACK (on_treeview_selection_change), ia);
265 gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view);
268 gtk_widget_show_all (scrolled_window);
270 ia->variable_names_cb = get_widget_assert (ia->text_builder, "variable-names");
272 reset_first_line_page (ia);
276 intro_on_leave (PsppireImportAssistant *ia, GtkWidget *page, enum IMPORT_ASSISTANT_DIRECTION dir)
278 if (dir != IMPORT_ASSISTANT_FORWARDS)
282 g_object_get (ia->text_file, "line-count", &lc, NULL);
283 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
285 gint max_lines = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin));
286 g_object_set (ia->text_file, "maximum-lines", max_lines, NULL);
288 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
290 gdouble percent = gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin));
291 g_object_set (ia->text_file, "maximum-lines", (gint) (lc * percent / 100.0), NULL);
295 g_object_set (ia->text_file, "maximum-lines", lc, NULL);
301 intro_on_enter (PsppireImportAssistant *ia, GtkWidget *page, enum IMPORT_ASSISTANT_DIRECTION dir)
303 GtkBuilder *builder = ia->text_builder;
304 GtkWidget *table = get_widget_assert (builder, "button-table");
309 ds_put_cstr (&s, _("This assistant will guide you through the process of "
310 "importing data into PSPP from a text file with one line "
311 "per case, in which fields are separated by tabs, "
312 "commas, or other delimiters.\n\n"));
316 if (ia->text_file->total_is_exact)
319 &s, ngettext ("The selected file contains %'lu line of text. ",
320 "The selected file contains %'lu lines of text. ",
321 ia->text_file->total_lines),
322 ia->text_file->total_lines);
324 else if (ia->text_file->total_lines > 0)
328 "The selected file contains approximately %'lu line of text. ",
329 "The selected file contains approximately %'lu lines of text. ",
330 ia->text_file->total_lines),
331 ia->text_file->total_lines);
334 "Only the first %zu line of the file will be shown for "
335 "preview purposes in the following screens. ",
336 "Only the first %zu lines of the file will be shown for "
337 "preview purposes in the following screens. ",
338 ia->text_file->n_lines),
339 ia->text_file->n_lines);
343 ds_put_cstr (&s, _("You may choose below how much of the file should "
344 "actually be imported."));
346 gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
350 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 1) == NULL)
352 GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin);
353 gtk_grid_attach (GTK_GRID (table), hbox_n_cases,
358 GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin));
359 gtk_adjustment_set_lower (adj, 1.0);
361 if (gtk_grid_get_child_at (GTK_GRID (table), 1, 2) == NULL)
363 GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"),
366 gtk_grid_attach (GTK_GRID (table), hbox_percent,
371 gtk_widget_show_all (table);
374 if (dir != IMPORT_ASSISTANT_FORWARDS)
377 reset_intro_page (ia);
378 on_intro_amount_changed (ia);
381 /* Initializes IA's intro substructure. */
383 intro_page_create (PsppireImportAssistant *ia)
385 GtkBuilder *builder = ia->text_builder;
387 GtkWidget *w = get_widget_assert (builder, "Intro");
389 ia->percent_spin = gtk_spin_button_new_with_range (0, 100, 1);
391 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Select the Lines to Import"));
393 ia->all_cases_button = get_widget_assert (builder, "import-all-cases");
394 ia->n_cases_button = get_widget_assert (builder, "import-n-cases");
395 ia->percent_button = get_widget_assert (builder, "import-percent");
397 g_signal_connect_swapped (ia->all_cases_button, "toggled",
398 G_CALLBACK (on_intro_amount_changed), ia);
399 g_signal_connect_swapped (ia->n_cases_button, "toggled",
400 G_CALLBACK (on_intro_amount_changed), ia);
401 g_signal_connect_swapped (ia->percent_button, "toggled",
402 G_CALLBACK (on_intro_amount_changed), ia);
404 g_object_set_data (G_OBJECT (w), "on-leaving", intro_on_leave);
405 g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter);
406 g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page);
412 /* Chooses a name for each column on the separators page */
414 choose_column_names (PsppireImportAssistant *ia)
417 unsigned long int generated_name_count = 0;
418 char *encoding = NULL;
419 g_object_get (ia->text_file, "encoding", &encoding, NULL);
421 dict_unref (ia->dict);
422 ia->dict = dict_create (encoding ? encoding : UTF8);
426 i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (ia->delimiters_model)) - 1;
429 const gchar *candidate_name = NULL;
431 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
433 candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i);
436 char *name = dict_make_unique_var_name (ia->dict,
438 &generated_name_count);
440 dict_create_var_assert (ia->dict, name, 0);
445 /* Called when the user toggles one of the separators
448 on_separator_toggle (GtkToggleButton *toggle UNUSED,
449 PsppireImportAssistant *ia)
451 revise_fields_preview (ia);
455 /* Called when the user changes the entry field for custom
458 on_separators_custom_entry_notify (GObject *gobject UNUSED,
459 GParamSpec *arg1 UNUSED,
460 PsppireImportAssistant *ia)
462 revise_fields_preview (ia);
465 /* Called when the user toggles the checkbox that enables custom
468 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
469 PsppireImportAssistant *ia)
471 bool is_active = gtk_toggle_button_get_active (custom_cb);
472 gtk_widget_set_sensitive (ia->custom_entry, is_active);
473 revise_fields_preview (ia);
476 /* Called when the user changes the selection in the combo box
477 that selects a quote character. */
479 on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia)
481 revise_fields_preview (ia);
484 /* Called when the user toggles the checkbox that enables
487 on_quote_cb_toggle (GtkToggleButton *quote_cb, PsppireImportAssistant *ia)
489 bool is_active = gtk_toggle_button_get_active (quote_cb);
490 gtk_widget_set_sensitive (ia->quote_combo, is_active);
491 revise_fields_preview (ia);
495 /* Called when the Reset button is clicked. */
497 reset_separators_page (PsppireImportAssistant *ia)
499 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->custom_cb), FALSE);
500 gtk_entry_set_text (GTK_ENTRY (ia->custom_entry), "");
501 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->quote_cb), TRUE);
502 gtk_combo_box_set_active (GTK_COMBO_BOX (ia->quote_combo), 0);
504 for (gint i = 0; i < N_SEPARATORS; i++)
506 const struct separator *s = &separators[i];
507 GtkWidget *button = get_widget_assert (ia->text_builder, s->name);
508 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), s->c == ',');
511 if (ia->delimiters_model)
513 repopulate_delimiter_columns (ia);
515 revise_fields_preview (ia);
519 /* Called just before the separators page becomes visible in the
522 prepare_separators_page (PsppireImportAssistant *ia, GtkWidget *new_page, enum IMPORT_ASSISTANT_DIRECTION dir)
524 if (dir != IMPORT_ASSISTANT_FORWARDS)
527 gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view),
528 GTK_TREE_MODEL (ia->delimiters_model));
530 g_signal_connect_swapped (GTK_TREE_MODEL (ia->delimiters_model), "notify::delimiters",
531 G_CALLBACK (reset_tree_view_model), ia);
534 reset_separators_page (ia);
538 /* Initializes IA's separators substructure. */
540 separators_page_create (PsppireImportAssistant *ia)
542 GtkBuilder *builder = ia->text_builder;
546 GtkWidget *w = get_widget_assert (builder, "Separators");
548 g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
549 g_object_set_data (G_OBJECT (w), "on-reset", reset_separators_page);
551 add_page_to_assistant (ia, w, GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
553 ia->custom_cb = get_widget_assert (builder, "custom-cb");
554 ia->custom_entry = get_widget_assert (builder, "custom-entry");
555 ia->quote_combo = get_widget_assert (builder, "quote-combo");
556 ia->quote_cb = get_widget_assert (builder, "quote-cb");
558 gtk_widget_set_sensitive (ia->custom_entry,
559 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->custom_cb)));
561 gtk_entry_set_max_length (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (ia->quote_combo))), 1);
563 if (ia->fields_tree_view == NULL)
565 GtkWidget *scroller = get_widget_assert (ia->text_builder, "fields-scroller");
566 ia->fields_tree_view = gtk_tree_view_new ();
567 g_object_set (ia->fields_tree_view, "enable-search", FALSE, NULL);
568 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
569 gtk_widget_show_all (scroller);
572 g_signal_connect (ia->quote_combo, "changed",
573 G_CALLBACK (on_quote_combo_change), ia);
574 g_signal_connect (ia->quote_cb, "toggled",
575 G_CALLBACK (on_quote_cb_toggle), ia);
576 g_signal_connect (ia->custom_entry, "notify::text",
577 G_CALLBACK (on_separators_custom_entry_notify), ia);
578 g_signal_connect (ia->custom_cb, "toggled",
579 G_CALLBACK (on_separators_custom_cb_toggle), ia);
580 for (i = 0; i < N_SEPARATORS; i++)
581 g_signal_connect (get_widget_assert (builder, separators[i].name),
582 "toggled", G_CALLBACK (on_separator_toggle), ia);
584 reset_separators_page (ia);
589 static struct casereader_random_class my_casereader_class;
591 static struct ccase *
592 my_read (struct casereader *reader, void *aux, casenumber idx)
594 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
595 GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
597 GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
599 const struct caseproto *proto = casereader_get_proto (reader);
602 struct ccase *c = NULL;
603 if (gtk_tree_model_get_iter (tm, &iter, tp))
605 c = case_create (proto);
607 for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
610 gtk_tree_model_get_value (tm, &iter, i + 1, &value);
612 const struct variable *var = dict_get_var (ia->casereader_dict, i);
614 const gchar *ss = g_value_get_string (&value);
617 union value *v = case_data_rw (c, var);
618 /* In this reader we derive the union value from the
619 string in the tree_model. We retrieve the width and format
620 from a dictionary which is stored directly after
621 the reader creation. Changes in ia->dict in the
622 variable window are not reflected here and therefore
623 this is always compatible with the width in the
624 caseproto. See bug #58298 */
625 char *xx = data_in (ss_cstr (ss),
627 var_get_write_format (var)->type,
628 settings_get_fmt_settings (),
629 v, var_get_width (var), "UTF-8");
633 g_value_unset (&value);
637 gtk_tree_path_free (tp);
643 my_destroy (struct casereader *reader, void *aux)
645 g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
649 my_advance (struct casereader *reader, void *aux, casenumber n)
651 g_print ("%s:%d\n", __FILE__, __LINE__);
654 static struct casereader *
655 textfile_create_reader (PsppireImportAssistant *ia)
657 int n_vars = dict_get_n_vars (ia->dict);
661 struct fmt_guesser **fg = XCALLOC (n_vars, struct fmt_guesser *);
662 for (i = 0 ; i < n_vars; ++i)
664 fg[i] = fmt_guesser_create ();
667 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
671 for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ia->delimiters_model), &iter);
673 ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->delimiters_model), &iter))
675 for (i = 0 ; i < n_vars; ++i)
678 gtk_tree_model_get (GTK_TREE_MODEL (ia->delimiters_model), &iter, i+1, &s, -1);
680 fmt_guesser_add (fg[i], ss_cstr (s));
685 struct caseproto *proto = caseproto_create ();
686 for (i = 0 ; i < n_vars; ++i)
689 fmt_guesser_guess (fg[i], &fs);
691 fmt_fix (&fs, FMT_FOR_INPUT);
693 struct variable *var = dict_get_var (ia->dict, i);
695 int width = fmt_var_width (&fs);
697 var_set_width_and_formats (var, width,
700 proto = caseproto_add_width (proto, width);
701 fmt_guesser_destroy (fg[i]);
706 struct casereader *cr = casereader_create_random (proto, n_rows, &my_casereader_class, ia);
707 /* Store the dictionary at this point when the casereader is created.
708 my_read depends on the dictionary to interpret the strings in the treeview.
709 This guarantees that the union value is produced according to the
710 caseproto in the reader. */
711 ia->casereader_dict = dict_clone (ia->dict);
712 caseproto_unref (proto);
717 /* When during import the variable type is changed, the reader is reinitialized
718 based on the new dictionary with a fresh caseprototype. The default behaviour
719 when a variable type is changed and the column is resized is that the union
720 value is interpreted with new variable type and an overlay for that column
721 is generated. Here we reinit to the original reader based on strings.
722 As a result you can switch from string to numeric to string without loosing
723 the string information. */
725 ia_variable_changed_cb (GObject *obj, gint var_num, guint what,
726 const struct variable *oldvar, gpointer data)
728 PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (data);
730 struct caseproto *proto = caseproto_create();
731 for (int i = 0; i < dict_get_n_vars (ia->dict); i++)
733 const struct variable *var = dict_get_var (ia->dict, i);
734 int width = var_get_width (var);
735 proto = caseproto_add_width (proto, width);
738 gint n_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (ia->delimiters_model), NULL);
740 PsppireDataStore *store = NULL;
741 g_object_get (ia->data_sheet, "data-model", &store, NULL);
743 struct casereader *cr = casereader_create_random (proto, n_rows,
744 &my_casereader_class, ia);
745 psppire_data_store_set_reader (store, cr);
746 dict_unref (ia->casereader_dict);
747 ia->casereader_dict = dict_clone (ia->dict);
751 /* Set the data model for both the data sheet and the variable sheet. */
753 textfile_set_data_models (PsppireImportAssistant *ia)
755 my_casereader_class.read = my_read;
756 my_casereader_class.destroy = my_destroy;
757 my_casereader_class.advance = my_advance;
759 struct casereader *reader = textfile_create_reader (ia);
761 PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
762 PsppireDataStore *store = psppire_data_store_new (dict);
763 psppire_data_store_set_reader (store, reader);
764 g_signal_connect (dict, "variable-changed",
765 G_CALLBACK (ia_variable_changed_cb),
768 g_object_set (ia->data_sheet, "data-model", store, NULL);
769 g_object_set (ia->var_sheet, "data-model", dict, NULL);
773 first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s)
776 g_object_get (ia->delimiters_model, "first-line", &first_case, NULL);
779 ds_put_format (s, " /FIRSTCASE=%d\n", first_case + 1);
782 /* Emits PSPP syntax to S that applies the dictionary attributes
783 (such as missing values and value labels) of the variables in
786 apply_dict (const struct dictionary *dict, struct string *s)
788 size_t n_vars = dict_get_n_vars (dict);
790 for (size_t i = 0; i < n_vars; i++)
792 struct variable *var = dict_get_var (dict, i);
793 const char *name = var_get_name (var);
794 enum val_type type = var_get_type (var);
795 int width = var_get_width (var);
796 enum measure measure = var_get_measure (var);
797 enum var_role role = var_get_role (var);
798 enum alignment alignment = var_get_alignment (var);
799 const struct fmt_spec *format = var_get_print_format (var);
801 if (var_has_missing_values (var))
803 const struct missing_values *mv = var_get_missing_values (var);
806 syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
807 for (j = 0; j < mv_n_values (mv); j++)
810 ds_put_cstr (s, ", ");
811 syntax_gen_value (s, mv_get_value (mv, j), width, format);
814 if (mv_has_range (mv))
817 if (mv_has_value (mv))
818 ds_put_cstr (s, ", ");
819 mv_get_range (mv, &low, &high);
820 syntax_gen_num_range (s, low, high, format);
822 ds_put_cstr (s, ").\n");
824 if (var_has_value_labels (var))
826 const struct val_labs *vls = var_get_value_labels (var);
827 const struct val_lab **labels = val_labs_sorted (vls);
828 size_t n_labels = val_labs_count (vls);
830 syntax_gen_pspp (s, "VALUE LABELS %ss", name);
831 for (size_t j = 0; j < n_labels; j++)
833 const struct val_lab *vl = labels[j];
834 ds_put_cstr (s, "\n ");
835 syntax_gen_value (s, &vl->value, width, format);
836 ds_put_byte (s, ' ');
837 syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
840 ds_put_cstr (s, ".\n");
842 if (var_has_label (var))
843 syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
844 name, var_get_label (var));
845 if (measure != var_default_measure (type))
846 syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
847 name, measure_to_syntax (measure));
848 if (role != ROLE_INPUT)
849 syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
850 var_role_to_syntax (role), name);
851 if (alignment != var_default_alignment (type))
852 syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
853 name, alignment_to_syntax (alignment));
854 if (var_get_display_width (var) != var_default_display_width (width))
855 syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
856 name, var_get_display_width (var));
862 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
865 g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
867 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
868 ds_put_format (s, "SELECT IF ($CASENUM <= %d).\n",
869 gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) - first_line);
870 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
871 ds_put_format (s, "SAMPLE %.4g.\n",
872 gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
877 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
879 g_return_if_fail (ia->dict);
881 ds_put_cstr (s, " /VARIABLES=\n");
883 int n_vars = dict_get_n_vars (ia->dict);
884 for (int i = 0; i < n_vars; i++)
886 struct variable *var = dict_get_var (ia->dict, i);
887 char format_string[FMT_STRING_LEN_MAX + 1];
888 fmt_to_string (var_get_print_format (var), format_string);
889 ds_put_format (s, " %s %s%s\n",
890 var_get_name (var), format_string,
891 i == n_vars - 1 ? "." : "");
896 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
900 ds_put_cstr (s, " /DELIMITERS=\"");
902 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->text_builder, "tab"))))
903 ds_put_cstr (s, "\\t");
904 for (i = 0; i < N_SEPARATORS; i++)
906 const struct separator *seps = &separators[i];
907 GtkWidget *button = get_widget_assert (ia->text_builder, seps->name);
908 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
913 ds_put_byte (s, seps->c);
916 ds_put_cstr (s, "\"\n");
918 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->quote_cb)))
920 GtkComboBoxText *cbt = GTK_COMBO_BOX_TEXT (ia->quote_combo);
921 gchar *quotes = gtk_combo_box_text_get_active_text (cbt);
922 if (quotes && *quotes)
923 syntax_gen_pspp (s, " /QUALIFIER=%sq\n", quotes);
930 text_spec_gen_syntax (PsppireImportAssistant *ia, struct string *s)
932 gchar *file_name = NULL;
933 gchar *encoding = NULL;
934 g_object_get (ia->text_file,
935 "file-name", &file_name,
936 "encoding", &encoding,
939 if (file_name == NULL)
947 if (encoding && strcmp (encoding, "Auto"))
948 syntax_gen_pspp (s, " /ENCODING=%sq\n", encoding);
951 " /ARRANGEMENT=DELIMITED\n"
954 first_line_append_syntax (ia, s);
955 separators_append_syntax (ia, s);
957 formats_append_syntax (ia, s);
958 apply_dict (ia->dict, s);
959 intro_append_syntax (ia, s);