Import Assistant: Fix generation of variable names
[pspp] / src / ui / gui / psppire-import-assistant.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2015, 2016, 2017  Free Software Foundation
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <gtk/gtk.h>
22 #include <sys/stat.h>
23
24 #include "data/casereader.h"
25 #include "data/data-in.h"
26 #include "data/data-out.h"
27 #include "data/dictionary.h"
28 #include "data/format-guesser.h"
29 #include "data/format.h"
30 #include "data/gnumeric-reader.h"
31 #include "data/ods-reader.h"
32 #include "data/spreadsheet-reader.h"
33 #include "data/value-labels.h"
34 #include "data/casereader-provider.h"
35
36 #include "gl/intprops.h"
37
38 #include "libpspp/i18n.h"
39 #include "libpspp/line-reader.h"
40 #include "libpspp/message.h"
41 #include "libpspp/hmap.h"
42 #include "libpspp/hash-functions.h"
43 #include "libpspp/str.h"
44
45 #include "builder-wrapper.h"
46 #include "helper.h"
47 #include "psppire-import-assistant.h"
48 #include "psppire-scanf.h"
49 #include "psppire-dialog.h"
50 #include "psppire-encoding-selector.h"
51 #include "psppire-spreadsheet-model.h"
52 #include "psppire-text-file.h"
53 #include "psppire-delimited-text.h"
54 #include "psppire-data-sheet.h"
55 #include "psppire-data-store.h"
56 #include "psppire-dict.h"
57 #include "psppire-variable-sheet.h"
58
59 #include "ui/syntax-gen.h"
60
61 #include <gettext.h>
62 #define _(msgid) gettext (msgid)
63 #define N_(msgid) msgid
64
65 enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */
66
67
68 /* Sets IA's separators substructure to match the widgets. */
69 static void split_fields (PsppireImportAssistant *ia);
70
71 /* Chooses a name for each column on the separators page */
72 static void choose_column_names (PsppireImportAssistant *ia);
73
74
75
76 static void intro_page_create (PsppireImportAssistant *ia);
77 static void first_line_page_create (PsppireImportAssistant *ia);
78
79 static void separators_page_create (PsppireImportAssistant *ia);
80 static void formats_page_create (PsppireImportAssistant *ia);
81
82 static void push_watch_cursor (PsppireImportAssistant *ia);
83 static void pop_watch_cursor (PsppireImportAssistant *ia);
84
85
86
87 static void psppire_import_assistant_init            (PsppireImportAssistant      *act);
88 static void psppire_import_assistant_class_init      (PsppireImportAssistantClass *class);
89
90 G_DEFINE_TYPE (PsppireImportAssistant, psppire_import_assistant, GTK_TYPE_ASSISTANT);
91
92
93 /* Properties */
94 enum
95   {
96     PROP_0,
97   };
98
99 static void
100 psppire_import_assistant_set_property (GObject         *object,
101                                        guint            prop_id,
102                                        const GValue    *value,
103                                        GParamSpec      *pspec)
104 {
105   //   PsppireImportAssistant *act = PSPPIRE_IMPORT_ASSISTANT (object);
106
107   switch (prop_id)
108     {
109     default:
110       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111       break;
112     };
113 }
114
115
116 static void
117 psppire_import_assistant_get_property (GObject    *object,
118                                        guint            prop_id,
119                                        GValue          *value,
120                                        GParamSpec      *pspec)
121 {
122   //  PsppireImportAssistant *assistant = PSPPIRE_IMPORT_ASSISTANT (object);
123
124   switch (prop_id)
125     {
126     default:
127       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
128       break;
129     };
130 }
131
132 static GObjectClass * parent_class = NULL;
133
134
135 static void
136 psppire_import_assistant_finalize (GObject *object)
137 {
138   PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (object);
139
140
141   if (ia->spreadsheet)
142     spreadsheet_unref (ia->spreadsheet);
143
144   ds_destroy (&ia->quotes);
145
146   g_object_unref (ia->builder);
147
148   if (G_OBJECT_CLASS (parent_class)->finalize)
149     G_OBJECT_CLASS (parent_class)->finalize (object);
150 }
151
152
153 static void
154 psppire_import_assistant_class_init (PsppireImportAssistantClass *class)
155 {
156   GObjectClass *object_class = G_OBJECT_CLASS (class);
157
158   parent_class = g_type_class_peek_parent (class);
159
160   object_class->set_property = psppire_import_assistant_set_property;
161   object_class->get_property = psppire_import_assistant_get_property;
162
163   object_class->finalize = psppire_import_assistant_finalize;
164 }
165
166
167 /* Causes the assistant to close, returning RESPONSE for
168    interpretation by text_data_import_assistant. */
169 static void
170 close_assistant (PsppireImportAssistant *ia, int response)
171 {
172   ia->response = response;
173   g_main_loop_quit (ia->main_loop);
174   gtk_widget_hide (GTK_WIDGET (ia));
175 }
176
177
178 /* Called when the Paste button on the last page of the assistant
179    is clicked. */
180 static void
181 on_paste (GtkButton *button, PsppireImportAssistant *ia)
182 {
183   close_assistant (ia, PSPPIRE_RESPONSE_PASTE);
184 }
185
186
187 /* Revises the contents of the fields tree view based on the
188    currently chosen set of separators. */
189 static void
190 revise_fields_preview (PsppireImportAssistant *ia)
191 {
192   choose_column_names (ia);
193 }
194
195
196 struct separator
197 {
198   const char *name;           /* Name (for use with get_widget_assert). */
199   gunichar c;                 /* Separator character. */
200 };
201
202 /* All the separators in the dialog box. */
203 static const struct separator separators[] =
204   {
205     {"space", ' '},
206     {"tab", '\t'},
207     {"bang", '!'},
208     {"colon", ':'},
209     {"comma", ','},
210     {"hyphen", '-'},
211     {"pipe", '|'},
212     {"semicolon", ';'},
213     {"slash", '/'},
214   };
215 #define SEPARATOR_CNT (sizeof separators / sizeof *separators)
216
217 struct separator_count_node
218 {
219   struct hmap_node node;
220   int occurance; /* The number of times the separator occurs in a line */
221   int quantity;  /* The number of lines with this occurance */
222 };
223
224
225 /* Picks the most likely separator and quote characters based on
226    IA's file data. */
227 static void
228 choose_likely_separators (PsppireImportAssistant *ia)
229 {
230   gint first_line = 0;
231   g_object_get (ia->delimiters_model, "first-line", &first_line, NULL);
232
233   gboolean valid;
234   GtkTreeIter iter;
235   int i = 0;
236
237   int j;
238
239   struct hmap count_map[SEPARATOR_CNT];
240   for (j = 0; j < SEPARATOR_CNT; ++j)
241     hmap_init (count_map + j);
242
243   GtkTreePath *p = gtk_tree_path_new_from_indices (first_line, -1);
244
245   for (valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (ia->text_file), &iter, p);
246        valid;
247        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (ia->text_file), &iter))
248     {
249       int j;
250
251       gchar *line_text = NULL;
252       gtk_tree_model_get (GTK_TREE_MODEL (ia->text_file), &iter, 1, &line_text, -1);
253
254       gint *counts = xzalloc (sizeof *counts * SEPARATOR_CNT);
255       for (j = 0; j < strlen (line_text); ++j)
256         {
257           int s;
258           for (s = 0; s < SEPARATOR_CNT; ++s)
259             {
260               // FIXME do this in UTF8 encoding
261               if (line_text[j] == separators[s].c)
262                 counts[s]++;
263             }
264         }
265
266       for (j = 0; j < SEPARATOR_CNT; ++j)
267         {
268           if (counts[j] > 0)
269             {
270               struct separator_count_node *cn = NULL;
271               unsigned int hash = hash_int (counts[j], 0);
272               HMAP_FOR_EACH_WITH_HASH (cn, struct separator_count_node, node, hash, &count_map[j])
273                 {
274                   if (cn->occurance == counts[j])
275                     break;
276                 }
277
278               if (cn == NULL)
279                 {
280                   struct separator_count_node *new_cn = xzalloc (sizeof *new_cn);
281                   new_cn->occurance = counts[j];
282                   new_cn->quantity = 1;
283                   hmap_insert (&count_map[j], &new_cn->node, hash);
284                 }
285               else
286                 cn->quantity++;
287             }
288         }
289
290       free (line_text);
291       free (counts);
292     }
293   gtk_tree_path_free (p);
294
295   int most_frequent = -1;
296   int largest = 0;
297   for (j = 0; j < SEPARATOR_CNT; ++j)
298     {
299       struct separator_count_node *cn;
300       HMAP_FOR_EACH (cn, struct separator_count_node, node, &count_map[j])
301         {
302           if (largest < cn->quantity)
303             {
304               largest = cn->quantity;
305               most_frequent = j;
306             }
307         }
308       hmap_destroy (&count_map[j]);
309     }
310
311   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, separators[most_frequent].name)), TRUE);
312 }
313
314 static void
315 repopulate_delimiter_columns (PsppireImportAssistant *ia)
316 {
317   /* Remove all the columns */
318   while (gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view)) > 0)
319     {
320       GtkTreeViewColumn *tvc = gtk_tree_view_get_column (GTK_TREE_VIEW (ia->fields_tree_view), 0);
321       gtk_tree_view_remove_column (GTK_TREE_VIEW (ia->fields_tree_view), tvc);
322     }
323
324   gint n_fields = gtk_tree_model_get_n_columns (ia->delimiters_model);
325
326   /* ... and put them back again. */
327   gint f;
328   for (f = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (ia->fields_tree_view));
329        f < n_fields; f++)
330     {
331       GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
332
333       const gchar *title = NULL;
334
335       if (f == 0)
336         title = _("line");
337       else
338         {
339           if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
340             {
341               title =
342                 psppire_delimited_text_get_header_title
343                 (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), f - 1);
344             }
345           if (title == NULL)
346             title = _("var");
347         }
348
349       GtkTreeViewColumn *column =
350         gtk_tree_view_column_new_with_attributes (title,
351                                                   renderer,
352                                                   "text", f,
353                                                   NULL);
354       g_object_set (column,
355                     "resizable", TRUE,
356                     "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
357                     NULL);
358
359       gtk_tree_view_append_column (GTK_TREE_VIEW (ia->fields_tree_view), column);
360     }
361 }
362
363 static void
364 reset_tree_view_model (PsppireImportAssistant *ia)
365 {
366   GtkTreeModel *tm = gtk_tree_view_get_model (GTK_TREE_VIEW (ia->fields_tree_view));
367   g_object_ref (tm);
368   gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), NULL);
369
370
371   repopulate_delimiter_columns (ia);
372
373   gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), tm);
374   //  gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ia->fields_tree_view));
375
376   g_object_unref (tm);
377 }
378
379 /* Called just before the separators page becomes visible in the
380    assistant, and when the Reset button is clicked. */
381 static void
382 prepare_separators_page (PsppireImportAssistant *ia, GtkWidget *page)
383 {
384   gtk_tree_view_set_model (GTK_TREE_VIEW (ia->fields_tree_view), ia->delimiters_model);
385
386   g_signal_connect_swapped (ia->delimiters_model, "notify::delimiters",
387                         G_CALLBACK (reset_tree_view_model), ia);
388
389
390   repopulate_delimiter_columns (ia);
391
392   revise_fields_preview (ia);
393   choose_likely_separators (ia);
394 }
395
396 /* Resets IA's intro page to its initial state. */
397 static void
398 reset_intro_page (PsppireImportAssistant *ia)
399 {
400   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ia->all_cases_button),
401                                 TRUE);
402 }
403
404
405
406 /* Clears the set of user-modified variables from IA's formats
407    substructure.  This discards user modifications to variable
408    formats, thereby causing formats to revert to their
409    defaults.  */
410 static void
411 reset_formats_page (PsppireImportAssistant *ia, GtkWidget *page)
412 {
413 }
414
415 static void prepare_formats_page (PsppireImportAssistant *ia);
416
417 /* Called when the Reset button is clicked. */
418 static void
419 on_reset (GtkButton *button, PsppireImportAssistant *ia)
420 {
421   gint pn = gtk_assistant_get_current_page (GTK_ASSISTANT (ia));
422   {
423     GtkWidget *page =  gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), pn);
424
425     page_func *on_reset = g_object_get_data (G_OBJECT (page), "on-reset");
426
427     if (on_reset)
428       on_reset (ia, page);
429   }
430 }
431
432
433 static gint
434 next_page_func (gint old_page, gpointer data)
435 {
436   return old_page + 1;
437 }
438
439
440 /* Called just before PAGE is displayed as the current page of
441    IMPORT_ASSISTANT, this updates IA content according to the new
442    page. */
443 static void
444 on_prepare (GtkAssistant *assistant, GtkWidget *page, PsppireImportAssistant *ia)
445 {
446   gtk_widget_show (ia->reset_button);
447   gtk_widget_hide (ia->paste_button);
448
449   gint pn = gtk_assistant_get_current_page (assistant);
450   gint previous_page_index = ia->current_page;
451
452   if (previous_page_index >= 0)
453     {
454       GtkWidget *closing_page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), previous_page_index);
455
456       if (pn > previous_page_index)
457         {
458           page_func *on_forward = g_object_get_data (G_OBJECT (closing_page), "on-forward");
459
460           if (on_forward)
461             on_forward (ia, closing_page);
462         }
463       else
464         {
465           page_func *on_back = g_object_get_data (G_OBJECT (closing_page), "on-back");
466
467           if (on_back)
468             on_back (ia, closing_page);
469         }
470     }
471
472   {
473     GtkWidget *new_page = gtk_assistant_get_nth_page (GTK_ASSISTANT (ia), pn);
474
475     page_func *on_entering = g_object_get_data (G_OBJECT (new_page), "on-entering");
476
477     if (on_entering)
478       on_entering (ia, new_page);
479   }
480
481   ia->current_page = pn;
482 }
483
484 /* Called when the Cancel button in the assistant is clicked. */
485 static void
486 on_cancel (GtkAssistant *assistant, PsppireImportAssistant *ia)
487 {
488   close_assistant (ia, GTK_RESPONSE_CANCEL);
489 }
490
491 /* Called when the Apply button on the last page of the assistant
492    is clicked. */
493 static void
494 on_close (GtkAssistant *assistant, PsppireImportAssistant *ia)
495 {
496   close_assistant (ia, GTK_RESPONSE_APPLY);
497 }
498
499
500 static GtkWidget *
501 add_page_to_assistant (PsppireImportAssistant *ia,
502                        GtkWidget *page, GtkAssistantPageType type, const gchar *);
503
504
505 static void
506 on_sheet_combo_changed (GtkComboBox *cb, PsppireImportAssistant *ia)
507 {
508   GtkTreeIter iter;
509   gchar *range = NULL;
510   GtkTreeModel *model = gtk_combo_box_get_model (cb);
511   GtkBuilder *builder = ia->builder;
512   GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
513
514   gtk_combo_box_get_active_iter (cb, &iter);
515   gtk_tree_model_get (model, &iter, PSPPIRE_SPREADSHEET_MODEL_COL_RANGE, &range, -1);
516   gtk_entry_set_text (GTK_ENTRY (range_entry), range ?  range : "");
517   g_free (range);
518 }
519
520 /* Prepares IA's sheet_spec page. */
521 static void
522 prepare_sheet_spec_page (PsppireImportAssistant *ia)
523 {
524   GtkBuilder *builder = ia->builder;
525   GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry");
526   GtkWidget *readnames_checkbox = get_widget_assert (builder, "readnames-checkbox");
527
528   gtk_combo_box_set_model (GTK_COMBO_BOX (sheet_entry),
529                            psppire_spreadsheet_model_new (ia->spreadsheet));
530
531   gtk_combo_box_set_active (GTK_COMBO_BOX (sheet_entry), 0);
532
533   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (readnames_checkbox), FALSE);
534 }
535
536
537 /* Initializes IA's sheet_spec substructure. */
538 static void
539 sheet_spec_page_create (PsppireImportAssistant *ia)
540 {
541   GtkBuilder *builder = ia->builder;
542   GtkWidget *page = get_widget_assert (builder, "Spreadsheet-Importer");
543
544   GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry");
545   GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
546   gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box));
547   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
548   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
549                                   "text", 0,
550                                   NULL);
551
552   g_signal_connect (combo_box, "changed", G_CALLBACK (on_sheet_combo_changed), ia);
553
554   add_page_to_assistant (ia, page,
555                          GTK_ASSISTANT_PAGE_CONTENT, _("Importing Spreadsheet Data"));
556
557   g_object_set_data (G_OBJECT (page), "on-entering", prepare_sheet_spec_page);
558 }
559
560 static void
561 on_chosen (PsppireImportAssistant *ia, GtkWidget *page)
562 {
563   GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
564   gchar *f = gtk_file_chooser_get_filename (fc);
565   int i;
566
567   for(i = gtk_assistant_get_n_pages (GTK_ASSISTANT (ia)); i > 0; --i)
568     gtk_assistant_remove_page (GTK_ASSISTANT (ia), i);
569
570   gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), FALSE);
571
572   if (f && !g_file_test (f, G_FILE_TEST_IS_DIR))
573     {
574       gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), GTK_WIDGET (fc), TRUE);
575
576       if (ia->spreadsheet)
577         spreadsheet_unref (ia->spreadsheet);
578
579       ia->spreadsheet = gnumeric_probe (f, FALSE);
580
581       if (!ia->spreadsheet)
582         ia->spreadsheet = ods_probe (f, FALSE);
583
584       if (!ia->spreadsheet)
585         {
586           intro_page_create (ia);
587           first_line_page_create (ia);
588           separators_page_create (ia);
589         }
590       else
591         {
592           sheet_spec_page_create (ia);
593         }
594
595       formats_page_create (ia);
596     }
597
598   g_free (f);
599 }
600
601 /* This has to be done on a map signal callback,
602    because GtkFileChooserWidget resets everything when it is mapped. */
603 static void
604 on_map (PsppireImportAssistant *ia, GtkWidget *page)
605 {
606 #if TEXT_FILE
607   GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
608
609   if (ia->file_name)
610     gtk_file_chooser_set_filename (fc, ia->file_name);
611 #endif
612
613   on_chosen (ia, page);
614 }
615
616
617
618 static void
619 chooser_page_enter (PsppireImportAssistant *ia, GtkWidget *page)
620 {
621 }
622
623 static void
624 chooser_page_leave (PsppireImportAssistant *ia, GtkWidget *page)
625 {
626   g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
627   g_free (ia->file_name);
628   ia->file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (page));
629   gchar *encoding = psppire_encoding_selector_get_encoding (ia->encoding_selector);
630
631   if (!ia->spreadsheet)
632     {
633       ia->text_file = psppire_text_file_new (ia->file_name, encoding);
634       gtk_tree_view_set_model (GTK_TREE_VIEW (ia->first_line_tree_view),
635                                GTK_TREE_MODEL (ia->text_file));
636     }
637
638
639   g_free (encoding);
640 }
641
642 static void
643 chooser_page_reset (PsppireImportAssistant *ia, GtkWidget *page)
644 {
645   GtkFileChooser *fc = GTK_FILE_CHOOSER (page);
646
647   gtk_file_chooser_set_filter (fc, ia->default_filter);
648   gtk_file_chooser_unselect_all (fc);
649
650   on_chosen (ia, page);
651 }
652
653
654
655 static void
656 chooser_page_create (PsppireImportAssistant *ia)
657 {
658   GtkFileFilter *filter = NULL;
659
660   GtkWidget *chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
661
662   g_object_set_data (G_OBJECT (chooser), "on-forward", chooser_page_leave);
663   g_object_set_data (G_OBJECT (chooser), "on-reset",   chooser_page_reset);
664   g_object_set_data (G_OBJECT (chooser), "on-entering",chooser_page_enter);
665
666   g_object_set (chooser, "local-only", FALSE, NULL);
667
668
669   ia->default_filter = gtk_file_filter_new ();
670   gtk_file_filter_set_name (ia->default_filter, _("All Files"));
671   gtk_file_filter_add_pattern (ia->default_filter, "*");
672   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), ia->default_filter);
673
674   filter = gtk_file_filter_new ();
675   gtk_file_filter_set_name (filter, _("Text Files"));
676   gtk_file_filter_add_mime_type (filter, "text/*");
677   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
678
679   filter = gtk_file_filter_new ();
680   gtk_file_filter_set_name (filter, _("Text (*.txt) Files"));
681   gtk_file_filter_add_pattern (filter, "*.txt");
682   gtk_file_filter_add_pattern (filter, "*.TXT");
683   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
684
685   filter = gtk_file_filter_new ();
686   gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files"));
687   gtk_file_filter_add_mime_type (filter, "text/plain");
688   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
689
690   filter = gtk_file_filter_new ();
691   gtk_file_filter_set_name (filter, _("Comma Separated Value Files"));
692   gtk_file_filter_add_mime_type (filter, "text/csv");
693   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
694
695   /* I've never encountered one of these, but it's listed here:
696      http://www.iana.org/assignments/media-types/text/tab-separated-values  */
697   filter = gtk_file_filter_new ();
698   gtk_file_filter_set_name (filter, _("Tab Separated Value Files"));
699   gtk_file_filter_add_mime_type (filter, "text/tab-separated-values");
700   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
701
702   filter = gtk_file_filter_new ();
703   gtk_file_filter_set_name (filter, _("Gnumeric Spreadsheet Files"));
704   gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
705   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
706
707   filter = gtk_file_filter_new ();
708   gtk_file_filter_set_name (filter, _("OpenDocument Spreadsheet Files"));
709   gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
710   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
711
712   filter = gtk_file_filter_new ();
713   gtk_file_filter_set_name (filter, _("All Spreadsheet Files"));
714   gtk_file_filter_add_mime_type (filter, "application/x-gnumeric");
715   gtk_file_filter_add_mime_type (filter, "application/vnd.oasis.opendocument.spreadsheet");
716   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
717
718   ia->encoding_selector = psppire_encoding_selector_new ("Auto", TRUE);
719   gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (chooser), ia->encoding_selector);
720
721   add_page_to_assistant (ia, chooser,
722                          GTK_ASSISTANT_PAGE_INTRO, _("Select File to Import"));
723
724   g_signal_connect_swapped (chooser, "selection-changed", G_CALLBACK (on_chosen), ia);
725   g_signal_connect_swapped (chooser, "map", G_CALLBACK (on_map), ia);
726 }
727
728
729
730 static void
731 psppire_import_assistant_init (PsppireImportAssistant *ia)
732 {
733   ia->builder = builder_new ("text-data-import.ui");
734
735   ia->current_page = -1 ;
736   ia->file_name = NULL;
737
738   ia->spreadsheet = NULL;
739
740   g_signal_connect (ia, "prepare", G_CALLBACK (on_prepare), ia);
741   g_signal_connect (ia, "cancel", G_CALLBACK (on_cancel), ia);
742   g_signal_connect (ia, "close", G_CALLBACK (on_close), ia);
743
744   ia->paste_button = gtk_button_new_with_label (_("Paste"));
745   ia->reset_button = gtk_button_new_with_label (_("Reset"));
746
747   gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->paste_button);
748
749   g_signal_connect (ia->paste_button, "clicked", G_CALLBACK (on_paste), ia);
750   g_signal_connect (ia->reset_button, "clicked", G_CALLBACK (on_reset), ia);
751
752   gtk_assistant_add_action_widget (GTK_ASSISTANT(ia), ia->reset_button);
753
754   gtk_window_set_title (GTK_WINDOW (ia),
755                         _("Importing Delimited Text Data"));
756
757   gtk_window_set_icon_name (GTK_WINDOW (ia), "pspp");
758
759   chooser_page_create (ia);
760
761   gtk_assistant_set_forward_page_func (GTK_ASSISTANT (ia), next_page_func, NULL, NULL);
762
763   gtk_window_maximize (GTK_WINDOW (ia));
764 }
765
766
767 /* Appends a page of the given TYPE, with PAGE as its content, to
768    the GtkAssistant encapsulated by IA.  Returns the GtkWidget
769    that represents the page. */
770 static GtkWidget *
771 add_page_to_assistant (PsppireImportAssistant *ia,
772                        GtkWidget *page, GtkAssistantPageType type, const gchar *title)
773 {
774   GtkWidget *content = page;
775
776   gtk_assistant_append_page (GTK_ASSISTANT (ia), content);
777   gtk_assistant_set_page_type (GTK_ASSISTANT(ia), content, type);
778   gtk_assistant_set_page_title (GTK_ASSISTANT(ia), content, title);
779   gtk_assistant_set_page_complete (GTK_ASSISTANT(ia), content, TRUE);
780
781   return content;
782 }
783
784
785 /* Called when one of the radio buttons is clicked. */
786 static void
787 on_intro_amount_changed (PsppireImportAssistant *p)
788 {
789   gtk_widget_set_sensitive (p->n_cases_spin,
790                             gtk_toggle_button_get_active (
791                                                           GTK_TOGGLE_BUTTON (p->n_cases_button)));
792
793   gtk_widget_set_sensitive (p->percent_spin,
794                             gtk_toggle_button_get_active (
795                                                           GTK_TOGGLE_BUTTON (p->percent_button)));
796 }
797
798
799
800 static void
801 on_treeview_selection_change (PsppireImportAssistant *ia)
802 {
803   GtkTreeSelection *selection =
804     gtk_tree_view_get_selection (GTK_TREE_VIEW (ia->first_line_tree_view));
805   GtkTreeModel *model = NULL;
806   GtkTreeIter iter;
807   if (gtk_tree_selection_get_selected (selection, &model, &iter))
808     {
809       int n;
810       GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
811       gint *index = gtk_tree_path_get_indices (path);
812
813       n = *index;
814
815       gtk_tree_path_free (path);
816
817       gtk_widget_set_sensitive (ia->variable_names_cb, n > 0);
818
819       ia->delimiters_model
820         = psppire_delimited_text_new (GTK_TREE_MODEL (ia->text_file));
821       g_object_set (ia->delimiters_model, "first-line", n, NULL);
822
823       g_print ("%s:%d DT %p first line %d\n", __FILE__, __LINE__, ia->delimiters_model, n);
824     }
825 }
826
827
828 /* Initializes IA's first_line substructure. */
829 static void
830 first_line_page_create (PsppireImportAssistant *ia)
831 {
832   g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
833   GtkWidget *w =  get_widget_assert (ia->builder, "FirstLine");
834
835   g_object_set_data (G_OBJECT (w), "on-entering", on_treeview_selection_change);
836
837   add_page_to_assistant (ia, w,
838                          GTK_ASSISTANT_PAGE_CONTENT, _("Select the First Line"));
839
840   GtkWidget *scrolled_window = get_widget_assert (ia->builder, "first-line-scroller");
841
842   if (ia->first_line_tree_view == NULL)
843     {
844       ia->first_line_tree_view = gtk_tree_view_new ();
845
846       gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ia->first_line_tree_view), TRUE);
847
848       GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
849       GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes (_("Line"), renderer,
850                                                                             "text", 0,
851                                                                             NULL);
852       gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
853
854       renderer = gtk_cell_renderer_text_new ();
855       column = gtk_tree_view_column_new_with_attributes (_("Text"), renderer, "text", 1, NULL);
856       gtk_tree_view_append_column (GTK_TREE_VIEW (ia->first_line_tree_view), column);
857
858       gtk_container_add (GTK_CONTAINER (scrolled_window), ia->first_line_tree_view);
859
860       g_signal_connect_swapped (ia->first_line_tree_view, "cursor-changed",
861                         G_CALLBACK (on_treeview_selection_change), ia);
862     }
863   gtk_widget_show_all (scrolled_window);
864
865   ia->variable_names_cb = get_widget_assert (ia->builder, "variable-names");
866
867 }
868
869
870
871
872 static void
873 intro_on_enter (PsppireImportAssistant *ia)
874 {
875   GtkBuilder *builder = ia->builder;
876   GtkWidget *table  = get_widget_assert (builder, "button-table");
877
878   struct string s;
879
880   ds_init_empty (&s);
881   ds_put_cstr (&s, _("This assistant will guide you through the process of "
882                      "importing data into PSPP from a text file with one line "
883                      "per case,  in which fields are separated by tabs, "
884                      "commas, or other delimiters.\n\n"));
885
886   if (ia->text_file)
887     {
888       if (ia->text_file->total_is_exact)
889         {
890           ds_put_format (
891                          &s, ngettext ("The selected file contains %'lu line of text.  ",
892                                        "The selected file contains %'lu lines of text.  ",
893                                        ia->text_file->total_lines),
894                          ia->text_file->total_lines);
895         }
896       else if (ia->text_file->total_lines > 0)
897         {
898           ds_put_format (
899                          &s, ngettext (
900                                        "The selected file contains approximately %'lu line of text.  ",
901                                        "The selected file contains approximately %'lu lines of text.  ",
902                                        ia->text_file->total_lines),
903                          ia->text_file->total_lines);
904           ds_put_format (
905                          &s, ngettext (
906                                        "Only the first %zu line of the file will be shown for "
907                                        "preview purposes in the following screens.  ",
908                                        "Only the first %zu lines of the file will be shown for "
909                                        "preview purposes in the following screens.  ",
910                                        ia->text_file->line_cnt),
911                          ia->text_file->line_cnt);
912         }
913     }
914
915   ds_put_cstr (&s, _("You may choose below how much of the file should "
916                      "actually be imported."));
917
918   gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")),
919                       ds_cstr (&s));
920   ds_destroy (&s);
921
922   GtkWidget *w  =  gtk_grid_get_child_at (GTK_GRID (table), 1, 1);
923   int old_value = w ? gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)) : 1;
924   if (w)
925     gtk_container_remove (GTK_CONTAINER (table), w);
926
927   w  =  gtk_grid_get_child_at (GTK_GRID (table), 1, 2);
928   if (w)
929     gtk_container_remove (GTK_CONTAINER (table), w);
930
931
932   GtkWidget *hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &ia->n_cases_spin);
933
934   GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (ia->n_cases_spin));
935   gtk_adjustment_set_lower (adj, 1.0);
936
937   if (ia->text_file)
938     {
939       if (psppire_text_file_get_total_exact (ia->text_file))
940         {
941           gulong total_lines = psppire_text_file_get_n_lines (ia->text_file);
942           gtk_adjustment_set_upper (adj, total_lines);
943           gtk_adjustment_set_value (adj, old_value);
944         }
945       else
946         gtk_adjustment_set_upper (adj, DBL_MAX);
947     }
948   gtk_grid_attach (GTK_GRID (table), hbox_n_cases,
949                    1, 1,
950                    1, 1);
951
952
953   GtkWidget *hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"),
954                                                &ia->percent_spin);
955
956   gtk_grid_attach (GTK_GRID (table), hbox_percent,
957                    1, 2,
958                    1, 1);
959
960   gtk_widget_show_all (table);
961
962   on_intro_amount_changed (ia);
963 }
964
965 /* Initializes IA's intro substructure. */
966 static void
967 intro_page_create (PsppireImportAssistant *ia)
968 {
969   GtkBuilder *builder = ia->builder;
970
971   GtkWidget *w =  get_widget_assert (builder, "Intro");
972
973   ia->percent_spin = gtk_spin_button_new_with_range (0, 100, 10);
974
975
976   add_page_to_assistant (ia, w,  GTK_ASSISTANT_PAGE_CONTENT, _("Select the Lines to Import"));
977
978   ia->all_cases_button = get_widget_assert (builder, "import-all-cases");
979
980   ia->n_cases_button = get_widget_assert (builder, "import-n-cases");
981
982   ia->percent_button = get_widget_assert (builder, "import-percent");
983
984   g_signal_connect_swapped (ia->all_cases_button, "toggled",
985                             G_CALLBACK (on_intro_amount_changed), ia);
986   g_signal_connect_swapped (ia->n_cases_button, "toggled",
987                             G_CALLBACK (on_intro_amount_changed), ia);
988   g_signal_connect_swapped (ia->percent_button, "toggled",
989                             G_CALLBACK (on_intro_amount_changed), ia);
990
991
992   g_object_set_data (G_OBJECT (w), "on-entering", intro_on_enter);
993   g_object_set_data (G_OBJECT (w), "on-reset", reset_intro_page);
994 }
995
996
997 GtkWidget *
998 psppire_import_assistant_new (GtkWindow *toplevel)
999 {
1000   return GTK_WIDGET (g_object_new (PSPPIRE_TYPE_IMPORT_ASSISTANT,
1001                                    /* Some window managers (notably ratpoison)
1002                                       ignore the maximise command when a window is
1003                                       transient.  This causes problems for this
1004                                       window. */
1005                                    /* "transient-for", toplevel, */
1006                                    NULL));
1007 }
1008
1009
1010
1011 \f
1012
1013 static void
1014 set_quote_list (GtkComboBox *cb)
1015 {
1016   GtkListStore *list =  gtk_list_store_new (1, G_TYPE_STRING);
1017   GtkTreeIter iter;
1018   gint i;
1019   const gchar *separator[3] = {"'\"", "\'", "\""};
1020
1021   for (i = 0; i < 3; i++)
1022     {
1023       const gchar *s = separator[i];
1024
1025       /* Add a new row to the model */
1026       gtk_list_store_append (list, &iter);
1027       gtk_list_store_set (list, &iter,
1028                           0, s,
1029                           -1);
1030
1031     }
1032
1033   gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list));
1034   g_object_unref (list);
1035
1036   gtk_combo_box_set_entry_text_column (cb, 0);
1037 }
1038
1039
1040
1041 /* Chooses a name for each column on the separators page */
1042 static void
1043 choose_column_names (PsppireImportAssistant *ia)
1044 {
1045   int i;
1046   unsigned long int generated_name_count = 0;
1047   dict_clear (ia->dict);
1048
1049   g_print ("%s:%d XXX %d\n", __FILE__, __LINE__, gtk_tree_model_get_n_columns (ia->delimiters_model));
1050       
1051   for (i = 0; i < gtk_tree_model_get_n_columns (ia->delimiters_model) - 1; ++i)
1052     {
1053       const gchar *candidate_name = NULL;
1054
1055       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->variable_names_cb)))
1056         {
1057           candidate_name = psppire_delimited_text_get_header_title (PSPPIRE_DELIMITED_TEXT (ia->delimiters_model), i);
1058         }
1059
1060       g_print ("%s:%d CN is %s\n", __FILE__, __LINE__, candidate_name);
1061
1062       char *name = dict_make_unique_var_name (ia->dict,
1063                                               candidate_name,
1064                                               &generated_name_count);
1065           
1066       dict_create_var_assert (ia->dict, name, 0);
1067       free (name);
1068     }
1069 }
1070
1071
1072
1073 /* Called when the user toggles one of the separators
1074    checkboxes. */
1075 static void
1076 on_separator_toggle (GtkToggleButton *toggle UNUSED,
1077                      PsppireImportAssistant *ia)
1078 {
1079   int i;
1080   GSList *delimiters = NULL;
1081   for (i = 0; i < SEPARATOR_CNT; i++)
1082     {
1083       const struct separator *s = &separators[i];
1084       GtkWidget *button = get_widget_assert (ia->builder, s->name);
1085       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1086         {
1087           delimiters = g_slist_prepend (delimiters,  GINT_TO_POINTER (s->c));
1088         }
1089     }
1090
1091   g_object_set (ia->delimiters_model, "delimiters", delimiters, NULL);
1092
1093   revise_fields_preview (ia);
1094 }
1095
1096
1097 /* Called when the user changes the entry field for custom
1098    separators. */
1099 static void
1100 on_separators_custom_entry_notify (GObject *gobject UNUSED,
1101                                    GParamSpec *arg1 UNUSED,
1102                                    PsppireImportAssistant *ia)
1103 {
1104   revise_fields_preview (ia);
1105 }
1106
1107 /* Called when the user toggles the checkbox that enables custom
1108    separators. */
1109 static void
1110 on_separators_custom_cb_toggle (GtkToggleButton *custom_cb,
1111                                 PsppireImportAssistant *ia)
1112 {
1113   bool is_active = gtk_toggle_button_get_active (custom_cb);
1114   gtk_widget_set_sensitive (ia->custom_entry, is_active);
1115   revise_fields_preview (ia);
1116 }
1117
1118 /* Called when the user changes the selection in the combo box
1119    that selects a quote character. */
1120 static void
1121 on_quote_combo_change (GtkComboBox *combo, PsppireImportAssistant *ia)
1122 {
1123   //  revise_fields_preview (ia);
1124 }
1125
1126 /* Called when the user toggles the checkbox that enables
1127    quoting. */
1128 static void
1129 on_quote_cb_toggle (GtkToggleButton *quote_cb, PsppireImportAssistant *ia)
1130 {
1131   bool is_active = gtk_toggle_button_get_active (quote_cb);
1132   gtk_widget_set_sensitive (ia->quote_combo, is_active);
1133   revise_fields_preview (ia);
1134 }
1135
1136 /* Initializes IA's separators substructure. */
1137 static void
1138 separators_page_create (PsppireImportAssistant *ia)
1139 {
1140   GtkBuilder *builder = ia->builder;
1141
1142   size_t i;
1143
1144   GtkWidget *w = get_widget_assert (builder, "Separators");
1145
1146   g_object_set_data (G_OBJECT (w), "on-entering", prepare_separators_page);
1147   g_object_set_data (G_OBJECT (w), "on-reset", prepare_separators_page);
1148
1149   add_page_to_assistant (ia, w,   GTK_ASSISTANT_PAGE_CONTENT, _("Choose Separators"));
1150
1151   ia->custom_cb = get_widget_assert (builder, "custom-cb");
1152   ia->custom_entry = get_widget_assert (builder, "custom-entry");
1153   ia->quote_combo = get_widget_assert (builder, "quote-combo");
1154   ia->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (ia->quote_combo)));
1155   ia->quote_cb = get_widget_assert (builder, "quote-cb");
1156
1157   set_quote_list (GTK_COMBO_BOX (ia->quote_combo));
1158
1159   if (ia->fields_tree_view == NULL)
1160     {
1161       GtkWidget *scroller = get_widget_assert (ia->builder, "fields-scroller");
1162       ia->fields_tree_view = gtk_tree_view_new ();
1163       gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ia->fields_tree_view));
1164       gtk_widget_show_all (scroller);
1165     }
1166
1167   g_signal_connect (ia->quote_combo, "changed",
1168                     G_CALLBACK (on_quote_combo_change), ia);
1169   g_signal_connect (ia->quote_cb, "toggled",
1170                     G_CALLBACK (on_quote_cb_toggle), ia);
1171   g_signal_connect (ia->custom_entry, "notify::text",
1172                     G_CALLBACK (on_separators_custom_entry_notify), ia);
1173   g_signal_connect (ia->custom_cb, "toggled",
1174                     G_CALLBACK (on_separators_custom_cb_toggle), ia);
1175   for (i = 0; i < SEPARATOR_CNT; i++)
1176     g_signal_connect (get_widget_assert (builder, separators[i].name),
1177                       "toggled", G_CALLBACK (on_separator_toggle), ia);
1178
1179 }
1180
1181
1182
1183 \f
1184
1185
1186 static struct casereader_random_class my_casereader_class;
1187
1188 static struct ccase *
1189 my_read (struct casereader *reader, void *aux, casenumber idx)
1190 {
1191   PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
1192   GtkTreeModel *tm = GTK_TREE_MODEL (ia->delimiters_model);
1193
1194   GtkTreePath *tp = gtk_tree_path_new_from_indices (idx, -1);
1195
1196   const struct caseproto *proto = casereader_get_proto (reader);
1197
1198   GtkTreeIter iter;
1199   struct ccase *c = NULL;
1200   if (gtk_tree_model_get_iter (tm, &iter, tp))
1201     {
1202       c = case_create (proto);
1203       int i;
1204       for (i = 0 ; i < caseproto_get_n_widths (proto); ++i)
1205         {
1206           GValue value = {0};
1207           gtk_tree_model_get_value (tm, &iter, i + 1, &value);
1208
1209           const struct variable *var = dict_get_var (ia->dict, i);
1210
1211           const gchar *ss = g_value_get_string (&value);
1212
1213           union value *v = case_data_rw (c, var);
1214           char *xx = data_in (ss_cstr (ss),
1215                               "UTF-8",
1216                               var_get_write_format (var)->type,
1217                               v, var_get_width (var), "UTF-8");
1218
1219           /* if (xx) */
1220           /*   g_print ("%s:%d Err %s\n", __FILE__, __LINE__, xx); */
1221           free (xx);
1222           g_value_unset (&value);
1223         }
1224     }
1225
1226   gtk_tree_path_free (tp);
1227
1228   return c;
1229 }
1230
1231 static void
1232 my_destroy (struct casereader *reader, void *aux)
1233 {
1234   g_print ("%s:%d %p\n", __FILE__, __LINE__, reader);
1235 }
1236
1237 static void
1238 my_advance (struct casereader *reader, void *aux, casenumber cnt)
1239 {
1240   g_print ("%s:%d\n", __FILE__, __LINE__);
1241 }
1242
1243 static void
1244 foo (struct dictionary *dict, void *aux)
1245 {
1246   PsppireImportAssistant *ia = PSPPIRE_IMPORT_ASSISTANT (aux);
1247   g_print ("%s:%d\n", __FILE__, __LINE__);
1248
1249   struct caseproto *proto = caseproto_create ();
1250
1251   int i;
1252   for (i = 0 ; i < dict_get_var_cnt (ia->dict); ++i)
1253     {
1254       const struct variable *var = dict_get_var (ia->dict, i);
1255       proto = caseproto_add_width (proto, var_get_width (var));
1256     }
1257
1258
1259   gint n_rows = gtk_tree_model_iter_n_children (ia->delimiters_model, NULL);
1260
1261   struct casereader *reader =
1262     casereader_create_random (proto, n_rows, &my_casereader_class,  ia);
1263
1264
1265   PsppireDataStore *store = NULL;
1266
1267   g_object_get (ia->data_sheet, "data-model", &store, NULL);
1268
1269   psppire_data_store_set_reader (store, reader);
1270 }
1271
1272 /* Called just before the formats page of the assistant is
1273    displayed. */
1274 static void
1275 prepare_formats_page (PsppireImportAssistant *ia)
1276 {
1277   PsppireDict *dict = psppire_dict_new_from_dict (ia->dict);
1278   g_object_set (ia->var_sheet, "data-model", dict, NULL);
1279
1280   my_casereader_class.read = my_read;
1281   my_casereader_class.destroy = my_destroy;
1282   my_casereader_class.advance = my_advance;
1283
1284   struct caseproto *proto = caseproto_create ();
1285   int i;
1286
1287   struct fmt_guesser **fg = xcalloc (sizeof *fg, dict_get_var_cnt (ia->dict));
1288   for (i = 0 ; i < dict_get_var_cnt (ia->dict); ++i)
1289     {
1290       fg[i] = fmt_guesser_create ();
1291     }
1292
1293   gint n_rows = gtk_tree_model_iter_n_children (ia->delimiters_model, NULL);
1294
1295   GtkTreeIter iter;
1296   gboolean ok;
1297   for (ok = gtk_tree_model_get_iter_first (ia->delimiters_model, &iter);
1298        ok;
1299        ok = gtk_tree_model_iter_next (ia->delimiters_model, &iter))
1300     {
1301       for (i = 0 ; i < dict_get_var_cnt (ia->dict); ++i)
1302         {
1303           gchar *s = NULL;
1304           gtk_tree_model_get (ia->delimiters_model, &iter, i+1, &s, -1);
1305           fmt_guesser_add (fg[i], ss_cstr (s));
1306           free (s);
1307         }
1308     }
1309
1310   for (i = 0 ; i < dict_get_var_cnt (ia->dict); ++i)
1311     {
1312       struct fmt_spec fs;
1313       fmt_guesser_guess (fg[i], &fs);
1314
1315       fmt_fix (&fs, FMT_FOR_INPUT);
1316
1317       struct variable *var = dict_get_var (ia->dict, i);
1318
1319       int width = fmt_var_width (&fs);
1320
1321       var_set_width_and_formats (var, width,
1322                                  &fs, &fs);
1323
1324       proto = caseproto_add_width (proto, width);
1325       fmt_guesser_destroy (fg[i]);
1326     }
1327
1328   free (fg);
1329
1330   //  dict_set_change_callback (ia->dict, foo, ia);
1331
1332   struct casereader *reader =
1333     casereader_create_random (proto, n_rows, &my_casereader_class,  ia);
1334
1335   PsppireDataStore *store = psppire_data_store_new (dict);
1336   psppire_data_store_set_reader (store, reader);
1337
1338   g_object_set (ia->data_sheet, "data-model", store, NULL);
1339
1340
1341   gint pmax;
1342   g_object_get (get_widget_assert (ia->builder, "vpaned1"),
1343                 "max-position", &pmax, NULL);
1344
1345
1346   g_object_set (get_widget_assert (ia->builder, "vpaned1"),
1347                 "position", pmax / 2, NULL);
1348
1349   gtk_widget_show (ia->paste_button);
1350 }
1351
1352 static void
1353 formats_page_create (PsppireImportAssistant *ia)
1354 {
1355   GtkBuilder *builder = ia->builder;
1356
1357   GtkWidget *w = get_widget_assert (builder, "Formats");
1358   g_object_set_data (G_OBJECT (w), "on-entering", prepare_formats_page);
1359   g_object_set_data (G_OBJECT (w), "on-reset", reset_formats_page);
1360
1361   GtkWidget *vars_scroller = get_widget_assert (builder, "vars-scroller");
1362   if (ia->var_sheet == NULL)
1363     {
1364       ia->var_sheet = psppire_variable_sheet_new ();
1365
1366       gtk_container_add (GTK_CONTAINER (vars_scroller), ia->var_sheet);
1367
1368       ia->dict = dict_create (get_default_encoding ());
1369
1370       gtk_widget_show_all (vars_scroller);
1371     }
1372   GtkWidget *data_scroller = get_widget_assert (builder, "data-scroller");
1373   if (ia->data_sheet == NULL)
1374     {
1375       ia->data_sheet = psppire_data_sheet_new ();
1376
1377       gtk_container_add (GTK_CONTAINER (data_scroller), ia->data_sheet);
1378
1379       gtk_widget_show_all (data_scroller);
1380     }
1381
1382   add_page_to_assistant (ia, w,
1383                          GTK_ASSISTANT_PAGE_CONFIRM, _("Adjust Variable Formats"));
1384 }
1385
1386
1387 \f
1388
1389 static void
1390 separators_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1391 {
1392   int i;
1393
1394   ds_put_cstr (s, "  /DELIMITERS=\"");
1395
1396   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (get_widget_assert (ia->builder, "tab"))))
1397     ds_put_cstr (s, "\\t");
1398   for (i = 0; i < SEPARATOR_CNT; i++)
1399     {
1400       const struct separator *seps = &separators[i];
1401       GtkWidget *button = get_widget_assert (ia->builder, seps->name);
1402       if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1403         {
1404           if (seps->c == '\t')
1405             continue;
1406
1407           ds_put_byte (s, seps->c);
1408         }
1409     }
1410   ds_put_cstr (s, "\"\n");
1411   if (!ds_is_empty (&ia->quotes))
1412     syntax_gen_pspp (s, "  /QUALIFIER=%sq\n", ds_cstr (&ia->quotes));
1413 }
1414
1415 static void
1416 formats_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1417 {
1418   int i;
1419   int var_cnt;
1420
1421   g_return_if_fail (ia->dict);
1422
1423   ds_put_cstr (s, "  /VARIABLES=\n");
1424
1425   var_cnt = dict_get_var_cnt (ia->dict);
1426   for (i = 0; i < var_cnt; i++)
1427     {
1428       struct variable *var = dict_get_var (ia->dict, i);
1429       char format_string[FMT_STRING_LEN_MAX + 1];
1430       fmt_to_string (var_get_print_format (var), format_string);
1431       ds_put_format (s, "    %s %s%s\n",
1432                      var_get_name (var), format_string,
1433                      i == var_cnt - 1 ? "." : "");
1434     }
1435 }
1436
1437 static void
1438 first_line_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1439 {
1440   gint first_case = 0;
1441   g_object_get (ia->delimiters_model, "first-line", &first_case, NULL);
1442
1443   if (first_case > 0)
1444     ds_put_format (s, "  /FIRSTCASE=%d\n", first_case + 1);
1445 }
1446
1447 static void
1448 intro_append_syntax (const PsppireImportAssistant *ia, struct string *s)
1449 {
1450   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->n_cases_button)))
1451     ds_put_format (s, "N OF CASES %d.\n",
1452                    gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (ia->n_cases_spin)));
1453   else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ia->percent_button)))
1454     ds_put_format (s, "SAMPLE %.4g.\n",
1455                    gtk_spin_button_get_value (GTK_SPIN_BUTTON (ia->percent_spin)) / 100.0);
1456 }
1457
1458
1459 /* Emits PSPP syntax to S that applies the dictionary attributes
1460    (such as missing values and value labels) of the variables in
1461    DICT.  */
1462 static void
1463 apply_dict (const struct dictionary *dict, struct string *s)
1464 {
1465   size_t var_cnt = dict_get_var_cnt (dict);
1466   size_t i;
1467
1468   for (i = 0; i < var_cnt; i++)
1469     {
1470       struct variable *var = dict_get_var (dict, i);
1471       const char *name = var_get_name (var);
1472       enum val_type type = var_get_type (var);
1473       int width = var_get_width (var);
1474       enum measure measure = var_get_measure (var);
1475       enum var_role role = var_get_role (var);
1476       enum alignment alignment = var_get_alignment (var);
1477       const struct fmt_spec *format = var_get_print_format (var);
1478
1479       if (var_has_missing_values (var))
1480         {
1481           const struct missing_values *mv = var_get_missing_values (var);
1482           size_t j;
1483
1484           syntax_gen_pspp (s, "MISSING VALUES %ss (", name);
1485           for (j = 0; j < mv_n_values (mv); j++)
1486             {
1487               if (j)
1488                 ds_put_cstr (s, ", ");
1489               syntax_gen_value (s, mv_get_value (mv, j), width, format);
1490             }
1491
1492           if (mv_has_range (mv))
1493             {
1494               double low, high;
1495               if (mv_has_value (mv))
1496                 ds_put_cstr (s, ", ");
1497               mv_get_range (mv, &low, &high);
1498               syntax_gen_num_range (s, low, high, format);
1499             }
1500           ds_put_cstr (s, ").\n");
1501         }
1502       if (var_has_value_labels (var))
1503         {
1504           const struct val_labs *vls = var_get_value_labels (var);
1505           const struct val_lab **labels = val_labs_sorted (vls);
1506           size_t n_labels = val_labs_count (vls);
1507           size_t i;
1508
1509           syntax_gen_pspp (s, "VALUE LABELS %ss", name);
1510           for (i = 0; i < n_labels; i++)
1511             {
1512               const struct val_lab *vl = labels[i];
1513               ds_put_cstr (s, "\n  ");
1514               syntax_gen_value (s, &vl->value, width, format);
1515               ds_put_byte (s, ' ');
1516               syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
1517             }
1518           free (labels);
1519           ds_put_cstr (s, ".\n");
1520         }
1521       if (var_has_label (var))
1522         syntax_gen_pspp (s, "VARIABLE LABELS %ss %sq.\n",
1523                          name, var_get_label (var));
1524       if (measure != var_default_measure (type))
1525         syntax_gen_pspp (s, "VARIABLE LEVEL %ss (%ss).\n",
1526                          name, measure_to_syntax (measure));
1527       if (role != ROLE_INPUT)
1528         syntax_gen_pspp (s, "VARIABLE ROLE /%ss %ss.\n",
1529                          var_role_to_syntax (role), name);
1530       if (alignment != var_default_alignment (type))
1531         syntax_gen_pspp (s, "VARIABLE ALIGNMENT %ss (%ss).\n",
1532                          name, alignment_to_syntax (alignment));
1533       if (var_get_display_width (var) != var_default_display_width (width))
1534         syntax_gen_pspp (s, "VARIABLE WIDTH %ss (%d).\n",
1535                          name, var_get_display_width (var));
1536     }
1537 }
1538
1539
1540
1541 static char *
1542 sheet_spec_gen_syntax (PsppireImportAssistant *ia)
1543 {
1544   GtkBuilder *builder = ia->builder;
1545   GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry");
1546   GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry");
1547   GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox");
1548   const gchar *range = gtk_entry_get_text (GTK_ENTRY (range_entry));
1549   int sheet_index = 1 + gtk_combo_box_get_active (GTK_COMBO_BOX (sheet_entry));
1550   gboolean read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc));
1551
1552   struct string s = DS_EMPTY_INITIALIZER;
1553
1554   char *filename;
1555   g_object_get (ia->text_file, "file-name", &filename, NULL);
1556   syntax_gen_pspp (&s,
1557                    "GET DATA"
1558                    "\n  /TYPE=%ss"
1559                    "\n  /FILE=%sq"
1560                    "\n  /SHEET=index %d"
1561                    "\n  /READNAMES=%ss",
1562                    (ia->spreadsheet->type == SPREADSHEET_GNUMERIC) ? "GNM" : "ODS",
1563                    filename,
1564                    sheet_index,
1565                    read_names ? "ON" : "OFF");
1566
1567   if (range && 0 != strcmp ("", range))
1568     {
1569       syntax_gen_pspp (&s,
1570                        "\n  /CELLRANGE=RANGE %sq", range);
1571     }
1572   else
1573     {
1574       syntax_gen_pspp (&s,
1575                        "\n  /CELLRANGE=FULL");
1576     }
1577
1578
1579   syntax_gen_pspp (&s, ".");
1580
1581
1582   return ds_cstr (&s);
1583 }
1584
1585
1586 gchar *
1587 psppire_import_assistant_generate_syntax (PsppireImportAssistant *ia)
1588 {
1589   struct string s = DS_EMPTY_INITIALIZER;
1590
1591   if (!ia->spreadsheet)
1592     {
1593       gchar *file_name = NULL;
1594       gchar *encoding = NULL;
1595       g_object_get (ia->text_file,
1596                     "file-name", &file_name,
1597                     "encoding", &encoding,
1598                     NULL);
1599
1600       if (file_name == NULL)
1601         return NULL;
1602
1603       syntax_gen_pspp (&s,
1604                        "GET DATA"
1605                        "\n  /TYPE=TXT"
1606                        "\n  /FILE=%sq\n",
1607                        file_name);
1608       if (encoding && strcmp (encoding, "Auto"))
1609         syntax_gen_pspp (&s, "  /ENCODING=%sq\n", encoding);
1610
1611       ds_put_cstr (&s,
1612                    "  /ARRANGEMENT=DELIMITED\n"
1613                    "  /DELCASE=LINE\n");
1614
1615       first_line_append_syntax (ia, &s);
1616       separators_append_syntax (ia, &s);
1617
1618       formats_append_syntax (ia, &s);
1619       apply_dict (ia->dict, &s);
1620       intro_append_syntax (ia, &s);
1621     }
1622   else
1623     {
1624       return sheet_spec_gen_syntax (ia);
1625     }
1626
1627   return ds_cstr (&s);
1628 }