X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fgui%2Ftext-data-import-dialog.c;h=a30178664c60578508ec09d3a3b9479fe75ac448;hb=0fe886a656b0ee57b4d542b7c16b3c56288b6fc6;hp=c674f17d1e1cd9279cdb472ae7e4d0ea6398d361;hpb=14aac9fe7a7efbb6c9bded2ed5969a643cb76645;p=pspp diff --git a/src/ui/gui/text-data-import-dialog.c b/src/ui/gui/text-data-import-dialog.c index c674f17d1e..a30178664c 100644 --- a/src/ui/gui/text-data-import-dialog.c +++ b/src/ui/gui/text-data-import-dialog.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2008, 2009 Free Software Foundation + Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,65 +16,56 @@ #include -#include - - - -#include "checkbox-treeview.h" -#include "descriptives-dialog.h" +#include "ui/gui/text-data-import-dialog.h" #include - -#include +#include +#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "executor.h" - -#include "error.h" -#include "xalloc.h" +#include "data/data-in.h" +#include "data/data-out.h" +#include "data/format-guesser.h" +#include "data/value-labels.h" +#include "language/data-io/data-parser.h" +#include "language/lexer/lexer.h" +#include "libpspp/assertion.h" +#include "libpspp/i18n.h" +#include "libpspp/line-reader.h" +#include "libpspp/message.h" +#include "ui/gui/builder-wrapper.h" +#include "ui/gui/checkbox-treeview.h" +#include "ui/gui/dialog-common.h" +#include "ui/gui/executor.h" +#include "ui/gui/helper.h" +#include "ui/gui/pspp-sheet-selection.h" +#include "ui/gui/pspp-sheet-view.h" +#include "ui/gui/psppire-data-window.h" +#include "ui/gui/psppire-dialog.h" +#include "ui/gui/psppire-encoding-selector.h" +#include "ui/gui/psppire-empty-list-store.h" +#include "ui/gui/psppire-scanf.h" +#include "ui/gui/psppire-var-sheet.h" +#include "ui/gui/psppire-var-store.h" +#include "ui/syntax-gen.h" + +#include "gl/error.h" +#include "gl/intprops.h" +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) #define N_(msgid) msgid - -/* TextImportModel, a GtkTreeModel used by the text data import - dialog. */ -enum - { - TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, /* 1-based line number in file */ - TEXT_IMPORT_MODEL_COLUMN_LINE, /* The line from the file. */ - }; -typedef struct TextImportModel TextImportModel; -typedef struct TextImportModelClass TextImportModelClass; - -TextImportModel *text_import_model_new (struct string *lines, size_t line_cnt, - size_t first_line); -gint text_import_model_iter_to_row (const GtkTreeIter *); - struct import_assistant; /* The file to be imported. */ struct file { char *file_name; /* File name. */ + gchar *encoding; /* Encoding. */ unsigned long int total_lines; /* Number of lines in file. */ bool total_is_exact; /* Is total_lines exact (or an estimate)? */ @@ -125,7 +116,7 @@ struct first_line_page bool variable_names; /* Variable names above first line of data? */ GtkWidget *page; - GtkTreeView *tree_view; + PsppSheetView *tree_view; GtkWidget *variable_names_cb; }; static void init_first_line_page (struct import_assistant *); @@ -150,7 +141,7 @@ struct separators_page GtkWidget *quote_combo; GtkEntry *quote_entry; GtkWidget *escape_cb; - GtkTreeView *fields_tree_view; + PsppSheetView *fields_tree_view; }; /* The columns that the separators divide the data into. */ struct column @@ -187,7 +178,7 @@ struct formats_page struct dictionary *dict; GtkWidget *page; - GtkTreeView *data_tree_view; + PsppSheetView *data_tree_view; PsppireDict *psppire_dict; struct variable **modified_vars; size_t modified_var_cnt; @@ -215,26 +206,26 @@ static gboolean get_tooltip_location (GtkWidget *widget, gint wx, gint wy, size_t *row, size_t *column); static void make_tree_view (const struct import_assistant *ia, size_t first_line, - GtkTreeView **tree_view); + PsppSheetView **tree_view); static void add_line_number_column (const struct import_assistant *, - GtkTreeView *); -static gint get_monospace_width (GtkTreeView *, GtkCellRenderer *, + PsppSheetView *); +static gint get_monospace_width (PsppSheetView *, GtkCellRenderer *, size_t char_cnt); -static gint get_string_width (GtkTreeView *, GtkCellRenderer *, +static gint get_string_width (PsppSheetView *, GtkCellRenderer *, const char *string); -static GtkTreeViewColumn *make_data_column (struct import_assistant *, - GtkTreeView *, bool input, +static PsppSheetViewColumn *make_data_column (struct import_assistant *, + PsppSheetView *, bool input, gint column_idx); -static GtkTreeView *create_data_tree_view (bool input, GtkContainer *parent, +static PsppSheetView *create_data_tree_view (bool input, GtkContainer *parent, struct import_assistant *); -static void escape_underscores (const char *in, char *out); static void push_watch_cursor (struct import_assistant *); static void pop_watch_cursor (struct import_assistant *); /* Pops up the Text Data Import assistant. */ void -text_data_import_assistant (GObject *o, GtkWindow *parent_window) +text_data_import_assistant (PsppireDataWindow *dw) { + GtkWindow *parent_window = GTK_WINDOW (dw); struct import_assistant *ia; ia = xzalloc (sizeof *ia); @@ -259,18 +250,10 @@ text_data_import_assistant (GObject *o, GtkWindow *parent_window) switch (ia->asst.response) { case GTK_RESPONSE_APPLY: - { - char *syntax = generate_syntax (ia); - execute_syntax (create_syntax_string_source (syntax)); - free (syntax); - } + free (execute_syntax_string (dw, generate_syntax (ia))); break; case PSPPIRE_RESPONSE_PASTE: - { - char *syntax = generate_syntax (ia); - paste_syntax_in_new_window (syntax); - free (syntax); - } + free (paste_syntax_to_window (generate_syntax (ia))); break; default: break; @@ -310,11 +293,9 @@ apply_dict (const struct dictionary *dict, struct string *s) syntax_gen_pspp (s, "MISSING VALUES %ss (", name); for (j = 0; j < mv_n_values (mv); j++) { - union value value; if (j) ds_put_cstr (s, ", "); - mv_get_value (mv, &value, j); - syntax_gen_value (s, &value, width, format); + syntax_gen_value (s, mv_get_value (mv, j), width, format); } if (mv_has_range (mv)) @@ -330,18 +311,20 @@ apply_dict (const struct dictionary *dict, struct string *s) if (var_has_value_labels (var)) { const struct val_labs *vls = var_get_value_labels (var); - struct val_labs_iterator *iter; - struct val_lab *vl; + const struct val_lab **labels = val_labs_sorted (vls); + size_t n_labels = val_labs_count (vls); + size_t i; syntax_gen_pspp (s, "VALUE LABELS %ss", name); - for (vl = val_labs_first_sorted (vls, &iter); vl != NULL; - vl = val_labs_next (vls, &iter)) + for (i = 0; i < n_labels; i++) { + const struct val_lab *vl = labels[i]; ds_put_cstr (s, "\n "); syntax_gen_value (s, &vl->value, width, format); - ds_put_char (s, ' '); - syntax_gen_string (s, ss_cstr (vl->label)); + ds_put_byte (s, ' '); + syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl))); } + free (labels); ds_put_cstr (s, ".\n"); } if (var_has_label (var)) @@ -380,6 +363,8 @@ generate_syntax (const struct import_assistant *ia) " /TYPE=TXT\n" " /FILE=%sq\n", ia->file.file_name); + if (ia->file.encoding && strcmp (ia->file.encoding, "Auto")) + syntax_gen_pspp (&s, " /ENCODING=%sq\n", ia->file.encoding); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON ( ia->intro.n_cases_button))) ds_put_format (&s, " /IMPORTCASES=FIRST %d\n", @@ -398,9 +383,9 @@ generate_syntax (const struct import_assistant *ia) if (ia->first_line.skip_lines > 0) ds_put_format (&s, " /FIRSTCASE=%d\n", ia->first_line.skip_lines + 1); ds_put_cstr (&s, " /DELIMITERS=\""); - if (ds_find_char (&ia->separators.separators, '\t') != SIZE_MAX) + if (ds_find_byte (&ia->separators.separators, '\t') != SIZE_MAX) ds_put_cstr (&s, "\\t"); - if (ds_find_char (&ia->separators.separators, '\\') != SIZE_MAX) + if (ds_find_byte (&ia->separators.separators, '\\') != SIZE_MAX) ds_put_cstr (&s, "\\\\"); for (i = 0; i < ds_length (&ia->separators.separators); i++) { @@ -408,7 +393,7 @@ generate_syntax (const struct import_assistant *ia) if (c == '"') ds_put_cstr (&s, "\"\""); else if (c != '\t' && c != '\\') - ds_put_char (&s, c); + ds_put_byte (&s, c); } ds_put_cstr (&s, "\"\n"); if (!ds_is_empty (&ia->separators.quotes)) @@ -435,7 +420,7 @@ generate_syntax (const struct import_assistant *ia) /* Choosing a file and reading it. */ -static char *choose_file (GtkWindow *parent_window); +static char *choose_file (GtkWindow *parent_window, gchar **encodingp); /* Obtains the file to import from the user and initializes IA's file substructure. PARENT_WINDOW must be the window to use @@ -449,16 +434,16 @@ init_file (struct import_assistant *ia, GtkWindow *parent_window) struct file *file = &ia->file; enum { MAX_PREVIEW_LINES = 1000 }; /* Max number of lines to read. */ enum { MAX_LINE_LEN = 16384 }; /* Max length of an acceptable line. */ - FILE *stream; + struct line_reader *reader; - file->file_name = choose_file (parent_window); + file->file_name = choose_file (parent_window, &file->encoding); if (file->file_name == NULL) return false; - stream = fopen (file->file_name, "r"); - if (stream == NULL) + reader = line_reader_for_file (file->encoding, file->file_name, O_RDONLY); + if (reader == NULL) { - msg (ME, _("Could not open \"%s\": %s"), + msg (ME, _("Could not open `%s': %s"), file->file_name, strerror (errno)); return false; } @@ -469,30 +454,29 @@ init_file (struct import_assistant *ia, GtkWindow *parent_window) struct string *line = &file->lines[file->line_cnt]; ds_init_empty (line); - if (!ds_read_line (line, stream, MAX_LINE_LEN)) + if (!line_reader_read (reader, line, MAX_LINE_LEN + 1) + || ds_length (line) > MAX_LINE_LEN) { - if (feof (stream)) + if (line_reader_eof (reader)) break; - else if (ferror (stream)) - msg (ME, _("Error reading \"%s\": %s"), - file->file_name, strerror (errno)); + else if (line_reader_error (reader)) + msg (ME, _("Error reading `%s': %s"), + file->file_name, strerror (line_reader_error (reader))); else - msg (ME, _("Failed to read \"%s\", because it contains a line " + msg (ME, _("Failed to read `%s', because it contains a line " "over %d bytes long and therefore appears not to be " "a text file."), file->file_name, MAX_LINE_LEN); - fclose (stream); + line_reader_close (reader); destroy_file (ia); return false; } - ds_chomp (line, '\n'); - ds_chomp (line, '\r'); } if (file->line_cnt == 0) { - msg (ME, _("\"%s\" is empty."), file->file_name); - fclose (stream); + msg (ME, _("`%s' is empty."), file->file_name); + line_reader_close (reader); destroy_file (ia); return false; } @@ -503,13 +487,15 @@ init_file (struct import_assistant *ia, GtkWindow *parent_window) else { struct stat s; - off_t position = ftello (stream); - if (fstat (fileno (stream), &s) == 0 && position > 0) + off_t position = line_reader_tell (reader); + if (fstat (line_reader_fileno (reader), &s) == 0 && position > 0) file->total_lines = (double) file->line_cnt / position * s.st_size; else file->total_lines = 0; } + line_reader_close (reader); + return true; } @@ -524,32 +510,78 @@ destroy_file (struct import_assistant *ia) ds_destroy (&f->lines[i]); free (f->lines); g_free (f->file_name); + g_free (f->encoding); } -/* Obtains the file to read from the user and returns the name of - the file as a string that must be freed with g_free if - successful, otherwise a null pointer. PARENT_WINDOW must be - the window to use as the file chooser window's parent. */ +/* Obtains the file to read from the user. If successful, returns the name of + the file and stores the user's chosen encoding for the file into *ENCODINGP. + The caller must free each of these strings with g_free(). + + On failure, stores a null pointer and stores NULL in *ENCODINGP. + + PARENT_WINDOW must be the window to use as the file chooser window's + parent. */ static char * -choose_file (GtkWindow *parent_window) +choose_file (GtkWindow *parent_window, gchar **encodingp) { - GtkWidget *dialog; char *file_name; + GtkFileFilter *filter = NULL; - dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"), + GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Import Delimited Text Data"), parent_window, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + g_object_set (dialog, "local-only", FALSE, NULL); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Text files")); + gtk_file_filter_add_mime_type (filter, "text/*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Text (*.txt) Files")); + gtk_file_filter_add_pattern (filter, "*.txt"); + gtk_file_filter_add_pattern (filter, "*.TXT"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Plain Text (ASCII) Files")); + gtk_file_filter_add_mime_type (filter, "text/plain"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Comma Separated Value Files")); + gtk_file_filter_add_mime_type (filter, "text/csv"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + /* I've never encountered one of these, but it's listed here: + http://www.iana.org/assignments/media-types/text/tab-separated-values */ + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("Tab Separated Value Files")); + gtk_file_filter_add_mime_type (filter, "text/tab-separated-values"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + + gtk_file_chooser_set_extra_widget ( + GTK_FILE_CHOOSER (dialog), psppire_encoding_selector_new ("Auto", true)); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All Files")); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); + switch (gtk_dialog_run (GTK_DIALOG (dialog))) { case GTK_RESPONSE_ACCEPT: file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + *encodingp = psppire_encoding_selector_get_encoding ( + gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog))); break; default: file_name = NULL; + *encodingp = NULL; break; } gtk_widget_destroy (dialog); @@ -589,7 +621,7 @@ init_assistant (struct import_assistant *ia, GtkWindow *parent_window) gtk_window_set_title (GTK_WINDOW (a->assistant), _("Importing Delimited Text Data")); gtk_window_set_transient_for (GTK_WINDOW (a->assistant), parent_window); - gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "psppicon"); + gtk_window_set_icon_name (GTK_WINDOW (a->assistant), "pspp"); a->prop_renderer = gtk_cell_renderer_text_new (); g_object_ref_sink (a->prop_renderer); @@ -720,8 +752,7 @@ close_assistant (struct import_assistant *ia, int response) /* The "intro" page of the assistant. */ -static void on_intro_amount_changed (GtkToggleButton *button, - struct import_assistant *); +static void on_intro_amount_changed (struct import_assistant *); /* Initializes IA's intro substructure. */ static void @@ -730,21 +761,47 @@ init_intro_page (struct import_assistant *ia) GtkBuilder *builder = ia->asst.builder; struct intro_page *p = &ia->intro; struct string s; + GtkWidget *hbox_n_cases ; + GtkWidget *hbox_percent ; + GtkWidget *table ; + + + p->n_cases_spin = gtk_spin_button_new_with_range (0, INT_MAX, 100); + + hbox_n_cases = psppire_scanf_new (_("Only the first %4d cases"), &p->n_cases_spin); + + table = get_widget_assert (builder, "button-table"); + + gtk_table_attach_defaults (GTK_TABLE (table), hbox_n_cases, + 1, 2, + 1, 2); + + p->percent_spin = gtk_spin_button_new_with_range (0, 100, 10); + + hbox_percent = psppire_scanf_new (_("Only the first %3d %% of file (approximately)"), &p->percent_spin); + + gtk_table_attach_defaults (GTK_TABLE (table), hbox_percent, + 1, 2, + 2, 3); p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"), GTK_ASSISTANT_PAGE_INTRO); + p->all_cases_button = get_widget_assert (builder, "import-all-cases"); + p->n_cases_button = get_widget_assert (builder, "import-n-cases"); - p->n_cases_spin = get_widget_assert (builder, "n-cases-spin"); + p->percent_button = get_widget_assert (builder, "import-percent"); - p->percent_spin = get_widget_assert (builder, "percent-spin"); - g_signal_connect (p->all_cases_button, "toggled", + + g_signal_connect_swapped (p->all_cases_button, "toggled", G_CALLBACK (on_intro_amount_changed), ia); - g_signal_connect (p->n_cases_button, "toggled", + g_signal_connect_swapped (p->n_cases_button, "toggled", G_CALLBACK (on_intro_amount_changed), ia); - g_signal_connect (p->percent_button, "toggled", + g_signal_connect_swapped (p->percent_button, "toggled", G_CALLBACK (on_intro_amount_changed), ia); + on_intro_amount_changed (ia); + ds_init_empty (&s); ds_put_cstr (&s, _("This assistant will guide you through the process of " "importing data into PSPP from a text file with one line " @@ -790,8 +847,7 @@ reset_intro_page (struct import_assistant *ia) /* Called when one of the radio buttons is clicked. */ static void -on_intro_amount_changed (GtkToggleButton *button UNUSED, - struct import_assistant *ia) +on_intro_amount_changed (struct import_assistant *ia) { struct intro_page *p = &ia->intro; @@ -799,16 +855,16 @@ on_intro_amount_changed (GtkToggleButton *button UNUSED, gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON (p->n_cases_button))); - gtk_widget_set_sensitive (ia->intro.percent_spin, + gtk_widget_set_sensitive (p->percent_spin, gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON (p->percent_button))); } /* The "first line" page of the assistant. */ -static GtkTreeView *create_lines_tree_view (GtkContainer *parent_window, +static PsppSheetView *create_lines_tree_view (GtkContainer *parent_window, struct import_assistant *); -static void on_first_line_change (GtkTreeSelection *, +static void on_first_line_change (PsppSheetSelection *, struct import_assistant *); static void on_variable_names_cb_toggle (GtkToggleButton *, struct import_assistant *); @@ -828,11 +884,12 @@ init_first_line_page (struct import_assistant *ia) p->tree_view = create_lines_tree_view ( GTK_CONTAINER (get_widget_assert (builder, "first-line-scroller")), ia); p->variable_names_cb = get_widget_assert (builder, "variable-names"); - gtk_tree_selection_set_mode ( - gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)), - GTK_SELECTION_BROWSE); + pspp_sheet_selection_set_mode ( + pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)), + PSPP_SHEET_SELECTION_BROWSE); + pspp_sheet_view_set_rubber_banding (PSPP_SHEET_VIEW (p->tree_view), TRUE); set_first_line (ia); - g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (p->tree_view)), + g_signal_connect (pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (p->tree_view)), "changed", G_CALLBACK (on_first_line_change), ia); g_signal_connect (p->variable_names_cb, "toggled", G_CALLBACK (on_variable_names_cb_toggle), ia); @@ -847,24 +904,42 @@ reset_first_line_page (struct import_assistant *ia) set_first_line (ia); } +static void +render_line (PsppSheetViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + gint row = empty_list_store_iter_to_row (iter); + struct string *lines; + + lines = g_object_get_data (G_OBJECT (tree_model), "lines"); + g_return_if_fail (lines != NULL); + + g_object_set (cell, "text", ds_cstr (&lines[row]), NULL); +} + + /* Creates and returns a tree view that contains each of the lines in IA's file as a row. */ -static GtkTreeView * +static PsppSheetView * create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia) { - GtkTreeView *tree_view; - GtkTreeViewColumn *column; + PsppSheetView *tree_view; + PsppSheetViewColumn *column; size_t max_line_length; gint content_width, header_width; size_t i; + const gchar *title = _("Text"); make_tree_view (ia, 0, &tree_view); - column = gtk_tree_view_column_new_with_attributes ( - "Text", ia->asst.fixed_renderer, - "text", TEXT_IMPORT_MODEL_COLUMN_LINE, - (void *) NULL); - gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + column = pspp_sheet_view_column_new_with_attributes ( + title, ia->asst.fixed_renderer, (void *) NULL); + pspp_sheet_view_column_set_cell_data_func (column, ia->asst.fixed_renderer, + render_line, NULL, NULL); + pspp_sheet_view_column_set_resizable (column, TRUE); max_line_length = 0; for (i = 0; i < ia->file.line_cnt; i++) @@ -875,12 +950,10 @@ create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia) content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer, max_line_length); - header_width = get_string_width (tree_view, ia->asst.prop_renderer, "Text"); - gtk_tree_view_column_set_fixed_width (column, MAX (content_width, + header_width = get_string_width (tree_view, ia->asst.prop_renderer, title); + pspp_sheet_view_column_set_fixed_width (column, MAX (content_width, header_width)); - gtk_tree_view_append_column (tree_view, column); - - gtk_tree_view_set_fixed_height_mode (tree_view, true); + pspp_sheet_view_append_column (tree_view, column); gtk_container_add (parent, GTK_WIDGET (tree_view)); gtk_widget_show (GTK_WIDGET (tree_view)); @@ -891,7 +964,7 @@ create_lines_tree_view (GtkContainer *parent, struct import_assistant *ia) /* Called when the line selected in the first_line tree view changes. */ static void -on_first_line_change (GtkTreeSelection *selection UNUSED, +on_first_line_change (PsppSheetSelection *selection UNUSED, struct import_assistant *ia) { get_first_line (ia); @@ -913,7 +986,7 @@ set_first_line (struct import_assistant *ia) GtkTreePath *path; path = gtk_tree_path_new_from_indices (ia->first_line.skip_lines, -1); - gtk_tree_view_set_cursor (GTK_TREE_VIEW (ia->first_line.tree_view), + pspp_sheet_view_set_cursor (PSPP_SHEET_VIEW (ia->first_line.tree_view), path, NULL, false); gtk_tree_path_free (path); @@ -928,12 +1001,12 @@ set_first_line (struct import_assistant *ia) static void get_first_line (struct import_assistant *ia) { - GtkTreeSelection *selection; + PsppSheetSelection *selection; GtkTreeIter iter; GtkTreeModel *model; - selection = gtk_tree_view_get_selection (ia->first_line.tree_view); - if (gtk_tree_selection_get_selected (selection, &model, &iter)) + selection = pspp_sheet_view_get_selection (ia->first_line.tree_view); + if (pspp_sheet_selection_get_selected (selection, &model, &iter)) { GtkTreePath *path = gtk_tree_model_get_path (model, &iter); int row = gtk_tree_path_get_indices (path)[0]; @@ -970,7 +1043,7 @@ static void on_quote_combo_change (GtkComboBox *combo, static void on_quote_cb_toggle (GtkToggleButton *quote_cb, struct import_assistant *); static void on_separator_toggle (GtkToggleButton *, struct import_assistant *); -static void render_input_cell (GtkTreeViewColumn *tree_column, +static void render_input_cell (PsppSheetViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer ia); @@ -1022,6 +1095,7 @@ set_quote_list (GtkComboBoxEntry *cb) } gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list)); + g_object_unref (list); gtk_combo_box_entry_set_text_column (cb, 0); } @@ -1047,7 +1121,7 @@ init_separators_page (struct import_assistant *ia) set_separators (ia); set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo)); - p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "fields")); + p->fields_tree_view = PSPP_SHEET_VIEW (get_widget_assert (builder, "fields")); g_signal_connect (p->quote_combo, "changed", G_CALLBACK (on_quote_combo_change), ia); g_signal_connect (p->quote_cb, "toggled", @@ -1142,7 +1216,7 @@ split_fields (struct import_assistant *ia) clear_fields (ia); /* Is space in the set of separators? */ - space_sep = ss_find_char (ds_ss (&s->separators), ' ') != SIZE_MAX; + space_sep = ss_find_byte (ds_ss (&s->separators), ' ') != SIZE_MAX; /* Split all the lines, not just those from ia->first_line.skip_lines on, so that we split the line that @@ -1169,9 +1243,9 @@ split_fields (struct import_assistant *ia) field = text; } else if (!ds_is_empty (&s->quotes) - && ds_find_char (&s->quotes, text.string[0]) != SIZE_MAX) + && ds_find_byte (&s->quotes, text.string[0]) != SIZE_MAX) { - int quote = ss_get_char (&text); + int quote = ss_get_byte (&text); if (!s->escape) ss_get_until (&text, quote, &field); else @@ -1180,18 +1254,18 @@ split_fields (struct import_assistant *ia) int c; ds_init_empty (&s); - while ((c = ss_get_char (&text)) != EOF) + while ((c = ss_get_byte (&text)) != EOF) if (c != quote) - ds_put_char (&s, c); - else if (ss_match_char (&text, quote)) - ds_put_char (&s, quote); + ds_put_byte (&s, c); + else if (ss_match_byte (&text, quote)) + ds_put_byte (&s, quote); else break; field = ds_ss (&s); } } else - ss_get_chars (&text, ss_cspan (text, ds_ss (&s->separators)), + ss_get_bytes (&text, ss_cspan (text, ds_ss (&s->separators)), &field); if (column_idx >= s->column_cnt) @@ -1216,7 +1290,7 @@ split_fields (struct import_assistant *ia) ss_ltrim (&text, ss_cstr (" ")); if (ss_is_empty (text)) break; - if (ss_find_char (ds_ss (&s->separators), ss_first (text)) + if (ss_find_byte (ds_ss (&s->separators), ss_first (text)) != SIZE_MAX) ss_advance (&text, 1); } @@ -1234,19 +1308,17 @@ choose_column_names (struct import_assistant *ia) struct column *col; size_t name_row; - dict = dict_create (); + dict = dict_create (get_default_encoding ()); name_row = f->variable_names && f->skip_lines ? f->skip_lines : 0; for (col = s->columns; col < &s->columns[s->column_cnt]; col++) { - char name[VAR_NAME_LEN + 1]; - char *hint; + char *hint, *name; hint = name_row ? ss_xstrdup (col->contents[name_row - 1]) : NULL; - if (!dict_make_unique_var_name (dict, hint, &generated_name_count, name)) - NOT_REACHED (); + name = dict_make_unique_var_name (dict, hint, &generated_name_count); free (hint); - col->name = xstrdup (name); + col->name = name; dict_create_var_assert (dict, name, 0); } dict_destroy (dict); @@ -1304,7 +1376,7 @@ find_commonest_chars (unsigned long int histogram[UCHAR_MAX + 1], if (max_count > 0) { ds_clear (result); - ds_put_char (result, max); + ds_put_byte (result, max); } else ds_assign_cstr (result, def); @@ -1360,7 +1432,7 @@ set_separators (struct import_assistant *ia) } } - ds_put_char (&custom, c); + ds_put_byte (&custom, c); next:; } @@ -1403,7 +1475,7 @@ get_separators (struct import_assistant *ia) const struct separator *sep = &separators[i]; GtkWidget *button = get_widget_assert (ia->asst.builder, sep->name); if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) - ds_put_char (&s->separators, sep->c); + ds_put_byte (&s->separators, sep->c); } if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->custom_cb))) @@ -1474,7 +1546,7 @@ on_separator_toggle (GtkToggleButton *toggle UNUSED, /* Called to render one of the cells in the fields preview tree view. */ static void -render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, +render_input_cell (PsppSheetViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer ia_) { @@ -1485,7 +1557,7 @@ render_input_cell (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column), "column-number")); - row = text_import_model_iter_to_row (iter) + ia->first_line.skip_lines; + row = empty_list_store_iter_to_row (iter) + ia->first_line.skip_lines; field = ia->separators.columns[column].contents[row]; if (field.string != NULL) { @@ -1540,9 +1612,10 @@ init_formats_page (struct import_assistant *ia) p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Formats"), GTK_ASSISTANT_PAGE_CONFIRM); - p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data")); + p->data_tree_view = PSPP_SHEET_VIEW (get_widget_assert (builder, "data")); p->modified_vars = NULL; p->modified_var_cnt = 0; + p->dict = NULL; } /* Frees IA's formats substructure. */ @@ -1566,7 +1639,6 @@ prepare_formats_page (struct import_assistant *ia) { struct dictionary *dict; PsppireDict *psppire_dict; - PsppireVarStore *var_store; GtkBin *vars_scroller; GtkWidget *old_var_sheet; PsppireVarSheet *var_sheet; @@ -1578,12 +1650,11 @@ prepare_formats_page (struct import_assistant *ia) push_watch_cursor (ia); - dict = dict_create (); + dict = dict_create (get_default_encoding ()); fg = fmt_guesser_create (); for (column_idx = 0; column_idx < s->column_cnt; column_idx++) { struct variable *modified_var; - char name[VAR_NAME_LEN + 1]; modified_var = (column_idx < p->modified_var_cnt ? p->modified_vars[column_idx] : NULL); @@ -1592,11 +1663,11 @@ prepare_formats_page (struct import_assistant *ia) struct column *column = &s->columns[column_idx]; struct variable *var; struct fmt_spec format; + char *name; size_t row; /* Choose variable name. */ - if (!dict_make_unique_var_name (dict, column->name, &number, name)) - NOT_REACHED (); + name = dict_make_unique_var_name (dict, column->name, &number); /* Choose variable format. */ fmt_guesser_clear (fg); @@ -1608,13 +1679,17 @@ prepare_formats_page (struct import_assistant *ia) /* Create variable. */ var = dict_create_var_assert (dict, name, fmt_var_width (&format)); var_set_both_formats (var, &format); + + free (name); } else { - if (!dict_make_unique_var_name (dict, var_get_name (modified_var), - &number, name)) - NOT_REACHED (); - dict_clone_var_assert (dict, modified_var, name); + char *name; + + name = dict_make_unique_var_name (dict, var_get_name (modified_var), + &number); + dict_clone_var_as_assert (dict, modified_var, name); + free (name); } } fmt_guesser_destroy (fg); @@ -1629,14 +1704,13 @@ prepare_formats_page (struct import_assistant *ia) psppire_dict for now, but it should. After it does, we should g_object_ref the psppire_dict here, since we also hold a reference via ia->formats.dict. */ - var_store = psppire_var_store_new (psppire_dict); - g_object_set (var_store, - "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS, - (void *) NULL); var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ()); g_object_set (var_sheet, - "model", var_store, + "dictionary", psppire_dict, "may-create-vars", FALSE, + "may-delete-vars", FALSE, + "format-use", FMT_FOR_INPUT, + "enable-grid-lines", PSPP_SHEET_VIEW_GRID_LINES_BOTH, (void *) NULL); vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller")); @@ -1688,14 +1762,14 @@ on_variable_change (PsppireDict *dict, int dict_idx, struct import_assistant *ia) { struct formats_page *p = &ia->formats; - GtkTreeView *tv = ia->formats.data_tree_view; + PsppSheetView *tv = ia->formats.data_tree_view; gint column_idx = dict_idx + 1; push_watch_cursor (ia); /* Remove previous column and replace with new column. */ - gtk_tree_view_remove_column (tv, gtk_tree_view_get_column (tv, column_idx)); - gtk_tree_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx), + pspp_sheet_view_remove_column (tv, pspp_sheet_view_get_column (tv, column_idx)); + pspp_sheet_view_insert_column (tv, make_data_column (ia, tv, false, dict_idx), column_idx); /* Save a copy of the modified variable in modified_vars, so @@ -1732,7 +1806,7 @@ parse_field (struct import_assistant *ia, char **outputp, char **tooltipp) { struct substring field; - union value *val; + union value val; struct variable *var; const struct fmt_spec *in; struct fmt_spec out; @@ -1741,39 +1815,36 @@ parse_field (struct import_assistant *ia, field = ia->separators.columns[column].contents[row]; var = dict_get_var (ia->formats.dict, column); - val = value_create (var_get_width (var)); + value_init (&val, var_get_width (var)); in = var_get_print_format (var); out = fmt_for_output_from_input (in); tooltip = NULL; if (field.string != NULL) { - msg_disable (); - if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, 0, - val, var_get_width (var))) + char *error; + + error = data_in (field, "UTF-8", in->type, &val, var_get_width (var), + dict_get_encoding (ia->formats.dict)); + if (error != NULL) { - char fmt_string[FMT_STRING_LEN_MAX + 1]; - fmt_to_string (in, fmt_string); - tooltip = xasprintf (_("Field content \"%.*s\" cannot be parsed in " - "format %s."), + tooltip = xasprintf (_("Cannot parse field content `%.*s' as " + "format %s: %s"), (int) field.length, field.string, - fmt_string); + fmt_name (in->type), error); + free (error); } - msg_enable (); } else { tooltip = xstrdup (_("This input line has too few separators " "to fill in this field.")); - value_set_missing (val, var_get_width (var)); + value_set_missing (&val, var_get_width (var)); } if (outputp != NULL) { - char *output = xmalloc (out.w + 1); - data_out (val, &out, output); - output[out.w] = '\0'; - *outputp = output; + *outputp = data_out (&val, dict_get_encoding (ia->formats.dict), &out); } - free (val); + value_destroy (&val, var_get_width (var)); ok = tooltip == NULL; if (tooltipp != NULL) @@ -1786,7 +1857,7 @@ parse_field (struct import_assistant *ia, /* Called to render one of the cells in the data preview tree view. */ static void -render_output_cell (GtkTreeViewColumn *tree_column, +render_output_cell (PsppSheetViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, @@ -1798,7 +1869,7 @@ render_output_cell (GtkTreeViewColumn *tree_column, bool ok; ok = parse_field (ia, - (text_import_model_iter_to_row (iter) + (empty_list_store_iter_to_row (iter) + ia->first_line.skip_lines), GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column), "column-number")), @@ -1846,11 +1917,11 @@ get_tooltip_location (GtkWidget *widget, gint wx, gint wy, const struct import_assistant *ia, size_t *row, size_t *column) { - GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); gint bx, by; GtkTreePath *path; GtkTreeIter iter; - GtkTreeViewColumn *tree_column; + PsppSheetViewColumn *tree_column; GtkTreeModel *tree_model; bool ok; @@ -1864,70 +1935,91 @@ get_tooltip_location (GtkWidget *widget, gint wx, gint wy, to make the data related to the tool tips part of a GObject that only gets destroyed when all references are released, but this solution appears to be effective too. */ - if (!GTK_WIDGET_MAPPED (widget)) + if (!gtk_widget_get_mapped (widget)) return FALSE; - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, + pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, wx, wy, &bx, &by); - if (!gtk_tree_view_get_path_at_pos (tree_view, bx, by, + if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by, &path, &tree_column, NULL, NULL)) return FALSE; *column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column), "column-number")); - tree_model = gtk_tree_view_get_model (tree_view); + tree_model = pspp_sheet_view_get_model (tree_view); ok = gtk_tree_model_get_iter (tree_model, &iter, path); gtk_tree_path_free (path); if (!ok) return FALSE; - *row = text_import_model_iter_to_row (&iter) + ia->first_line.skip_lines; + *row = empty_list_store_iter_to_row (&iter) + ia->first_line.skip_lines; return TRUE; } static void make_tree_view (const struct import_assistant *ia, size_t first_line, - GtkTreeView **tree_view) + PsppSheetView **tree_view) { GtkTreeModel *model; - *tree_view = GTK_TREE_VIEW (gtk_tree_view_new ()); - model = GTK_TREE_MODEL (text_import_model_new ( - ia->file.lines + first_line, - ia->file.line_cnt - first_line, first_line)); - gtk_tree_view_set_model (*tree_view, model); + *tree_view = PSPP_SHEET_VIEW (pspp_sheet_view_new ()); + pspp_sheet_view_set_grid_lines (*tree_view, PSPP_SHEET_VIEW_GRID_LINES_BOTH); + model = GTK_TREE_MODEL (psppire_empty_list_store_new ( + ia->file.line_cnt - first_line)); + g_object_set_data (G_OBJECT (model), "lines", ia->file.lines + first_line); + g_object_set_data (G_OBJECT (model), "first-line", + GINT_TO_POINTER (first_line)); + pspp_sheet_view_set_model (*tree_view, model); + g_object_unref (model); add_line_number_column (ia, *tree_view); } +static void +render_line_number (PsppSheetViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + gint row = empty_list_store_iter_to_row (iter); + char s[INT_BUFSIZE_BOUND (int)]; + int first_line; + + first_line = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_model), + "first-line")); + sprintf (s, "%d", first_line + row); + g_object_set (cell, "text", s, NULL); +} + static void add_line_number_column (const struct import_assistant *ia, - GtkTreeView *treeview) + PsppSheetView *treeview) { - GtkTreeViewColumn *column; + PsppSheetViewColumn *column; - column = gtk_tree_view_column_new_with_attributes ( - "Line", ia->asst.prop_renderer, - "text", TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER, - (void *) NULL); - gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); - gtk_tree_view_column_set_fixed_width ( + column = pspp_sheet_view_column_new_with_attributes ( + _("Line"), ia->asst.prop_renderer, (void *) NULL); + pspp_sheet_view_column_set_fixed_width ( column, get_monospace_width (treeview, ia->asst.prop_renderer, 5)); - gtk_tree_view_append_column (treeview, column); + pspp_sheet_view_column_set_resizable (column, TRUE); + pspp_sheet_view_column_set_cell_data_func (column, ia->asst.prop_renderer, + render_line_number, NULL, NULL); + pspp_sheet_view_append_column (treeview, column); } static gint -get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer, +get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer, size_t char_cnt) { struct string s; gint width; ds_init_empty (&s); - ds_put_char_multiple (&s, '0', char_cnt); - ds_put_char (&s, ' '); + ds_put_byte_multiple (&s, '0', char_cnt); + ds_put_byte (&s, ' '); width = get_string_width (treeview, renderer, ds_cstr (&s)); ds_destroy (&s); @@ -1935,7 +2027,7 @@ get_monospace_width (GtkTreeView *treeview, GtkCellRenderer *renderer, } static gint -get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer, +get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer, const char *string) { gint width; @@ -1945,65 +2037,66 @@ get_string_width (GtkTreeView *treeview, GtkCellRenderer *renderer, return width; } -static GtkTreeViewColumn * -make_data_column (struct import_assistant *ia, GtkTreeView *tree_view, +static PsppSheetViewColumn * +make_data_column (struct import_assistant *ia, PsppSheetView *tree_view, bool input, gint dict_idx) { struct variable *var = NULL; struct column *column = NULL; - char name[(VAR_NAME_LEN * 2) + 1]; size_t char_cnt; gint content_width, header_width; - GtkTreeViewColumn *tree_column; + PsppSheetViewColumn *tree_column; + char *name; if (input) column = &ia->separators.columns[dict_idx]; else var = dict_get_var (ia->formats.dict, dict_idx); - escape_underscores (input ? column->name : var_get_name (var), name); + name = escape_underscores (input ? column->name : var_get_name (var)); char_cnt = input ? column->width : var_get_print_format (var)->w; content_width = get_monospace_width (tree_view, ia->asst.fixed_renderer, char_cnt); header_width = get_string_width (tree_view, ia->asst.prop_renderer, name); - tree_column = gtk_tree_view_column_new (); + tree_column = pspp_sheet_view_column_new (); g_object_set_data (G_OBJECT (tree_column), "column-number", GINT_TO_POINTER (dict_idx)); - gtk_tree_view_column_set_title (tree_column, name); - gtk_tree_view_column_pack_start (tree_column, ia->asst.fixed_renderer, + pspp_sheet_view_column_set_title (tree_column, name); + pspp_sheet_view_column_pack_start (tree_column, ia->asst.fixed_renderer, FALSE); - gtk_tree_view_column_set_cell_data_func ( + pspp_sheet_view_column_set_cell_data_func ( tree_column, ia->asst.fixed_renderer, input ? render_input_cell : render_output_cell, ia, NULL); - gtk_tree_view_column_set_sizing (tree_column, GTK_TREE_VIEW_COLUMN_FIXED); - gtk_tree_view_column_set_fixed_width (tree_column, MAX (content_width, + pspp_sheet_view_column_set_fixed_width (tree_column, MAX (content_width, header_width)); + pspp_sheet_view_column_set_resizable (tree_column, TRUE); + + free (name); return tree_column; } -static GtkTreeView * +static PsppSheetView * create_data_tree_view (bool input, GtkContainer *parent, struct import_assistant *ia) { - GtkTreeView *tree_view; + PsppSheetView *tree_view; gint i; make_tree_view (ia, ia->first_line.skip_lines, &tree_view); - gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view), - GTK_SELECTION_NONE); + pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (tree_view), + PSPP_SHEET_SELECTION_NONE); for (i = 0; i < ia->separators.column_cnt; i++) - gtk_tree_view_append_column (tree_view, + pspp_sheet_view_append_column (tree_view, make_data_column (ia, tree_view, input, i)); g_object_set (G_OBJECT (tree_view), "has-tooltip", TRUE, (void *) NULL); g_signal_connect (tree_view, "query-tooltip", G_CALLBACK (input ? on_query_input_tooltip : on_query_output_tooltip), ia); - gtk_tree_view_set_fixed_height_mode (tree_view, true); gtk_container_add (parent, GTK_WIDGET (tree_view)); gtk_widget_show (GTK_WIDGET (tree_view)); @@ -2011,284 +2104,6 @@ create_data_tree_view (bool input, GtkContainer *parent, return tree_view; } -static void -escape_underscores (const char *in, char *out) -{ - for (; *in != '\0'; in++) - { - if (*in == '_') - *out++ = '_'; - *out++ = *in; - } - *out = '\0'; -} - -/* TextImportModel, a GtkTreeModel implementation used by some - pages of the assistant. */ - -#define G_TYPE_TEXT_IMPORT_MODEL (text_import_model_get_type ()) -#define TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_TEXT_IMPORT_MODEL, TextImportModel)) -#define TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass)) -#define IS_TEXT_IMPORT_MODEL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_TEXT_IMPORT_MODEL)) -#define IS_TEXT_IMPORT_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_TEXT_IMPORT_MODEL)) -#define TEXT_IMPORT_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_TEXT_IMPORT_MODEL, TextImportModelClass)) - -/* Random number used in 'stamp' member of GtkTreeIter. */ -#define TREE_MODEL_STAMP 0x7efd67d3 - -struct TextImportModel -{ - GObject parent; - struct string *lines; - size_t line_cnt; - size_t first_line; -}; - -struct TextImportModelClass -{ - GObjectClass parent_class; -}; - -GType text_import_model_get_type (void); -static void text_import_model_tree_model_init (gpointer iface, gpointer data); - -GType -text_import_model_get_type (void) -{ - static GType object_type = 0; - - if (!object_type) - { - static const GTypeInfo object_info = { - sizeof (TextImportModelClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - NULL, /* class_init */ - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (TextImportModel), - 0, /* n_preallocs */ - NULL, /* instance_init */ - }; - - static const GInterfaceInfo tree_model_info = { - text_import_model_tree_model_init, - NULL, - NULL - }; - - object_type = g_type_register_static (G_TYPE_OBJECT, - "TextImportModel", - &object_info, 0); - - g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL, - &tree_model_info); - - - } - - return object_type; -} - - -/* Creates and returns a new TextImportModel that contains the - LINE_CNT lines in LINES. The lines before FIRST_LINE in LINES - are not part of the model, but they are included in the line - numbers in the TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER column. - - The caller retains responsibility for freeing LINES and must - ensure that its lifetime and that of the strings that it - contains exceeds that of the TextImportModel. */ -TextImportModel * -text_import_model_new (struct string *lines, size_t line_cnt, - size_t first_line) -{ - TextImportModel *new_text_import_model - = g_object_new (G_TYPE_TEXT_IMPORT_MODEL, NULL); - new_text_import_model->lines = lines; - new_text_import_model->line_cnt = line_cnt; - new_text_import_model->first_line = first_line; - return new_text_import_model; -} - - -static gboolean -tree_model_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - return FALSE; -} - -static gboolean -tree_model_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - return TRUE; -} - -static GtkTreeModelFlags -tree_model_get_flags (GtkTreeModel *model) -{ - g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), (GtkTreeModelFlags) 0); - - return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST; -} - - -static gint -tree_model_n_columns (GtkTreeModel *model) -{ - return 2; -} - -static GType -tree_model_column_type (GtkTreeModel *model, gint index) -{ - return (index == TEXT_IMPORT_MODEL_COLUMN_LINE_NUMBER ? G_TYPE_INT - : index == TEXT_IMPORT_MODEL_COLUMN_LINE ? G_TYPE_STRING - : -1); -} - -static gboolean -init_iter (TextImportModel *list, gint idx, GtkTreeIter *iter) -{ - if (idx < 0 || idx >= list->line_cnt) - { - iter->stamp = 0; - iter->user_data = GINT_TO_POINTER (-1); - return FALSE; - } - else - { - iter->stamp = TREE_MODEL_STAMP; - iter->user_data = GINT_TO_POINTER (idx); - return TRUE; - } -} - -static gboolean -tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path) -{ - gint *indices, depth; - - TextImportModel *list = TEXT_IMPORT_MODEL (model); - - g_return_val_if_fail (path, FALSE); - - indices = gtk_tree_path_get_indices (path); - depth = gtk_tree_path_get_depth (path); - - g_return_val_if_fail (depth == 1, FALSE); - - return init_iter (list, indices[0], iter); -} - - -static gboolean -tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter) -{ - TextImportModel *list = TEXT_IMPORT_MODEL (model); - gint idx; - - assert (iter->stamp == TREE_MODEL_STAMP); - - idx = GPOINTER_TO_INT (iter->user_data); - return init_iter (list, idx == -1 ? -1 : idx + 1, iter); -} - -static GtkTreePath * -tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter) -{ - GtkTreePath *path; - - g_return_val_if_fail (iter->stamp == TREE_MODEL_STAMP, FALSE); - - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, GPOINTER_TO_INT (iter->user_data)); - - return path; -} - -static void -tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, - gint column, GValue *value) -{ - TextImportModel *list = TEXT_IMPORT_MODEL (model); - gint idx; - - g_return_if_fail (iter->stamp == TREE_MODEL_STAMP); - - idx = GPOINTER_TO_INT (iter->user_data); - assert (idx >= 0 && idx < list->line_cnt); - - if (column == 0) - { - g_value_init (value, G_TYPE_INT); - g_value_set_int (value, idx + list->first_line + 1); - } - else - { - g_value_init (value, G_TYPE_STRING); - g_value_set_static_string (value, ds_cstr (&list->lines[idx])); - } -} - -static gboolean -tree_model_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - return FALSE; -} - -static gint -tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter) -{ - TextImportModel *list = TEXT_IMPORT_MODEL (model); - - return iter == NULL ? list->line_cnt : 0; -} - -static gboolean -tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter, - GtkTreeIter *parent, gint n) -{ - TextImportModel *list = TEXT_IMPORT_MODEL (model); - g_return_val_if_fail (IS_TEXT_IMPORT_MODEL (model), FALSE); - - if (parent) - return FALSE; - return init_iter (list, n, iter); -} - -static void -text_import_model_tree_model_init (gpointer iface_, gpointer data UNUSED) -{ - GtkTreeModelIface *iface = (GtkTreeModelIface *) iface_; - - iface->get_flags = tree_model_get_flags; - iface->get_n_columns = tree_model_n_columns; - iface->get_column_type = tree_model_column_type; - iface->get_iter = tree_model_get_iter; - iface->iter_next = tree_model_iter_next; - iface->get_path = tree_model_get_path; - iface->get_value = tree_model_get_value; - - iface->iter_children = tree_model_iter_children; - iface->iter_has_child = tree_model_iter_has_child; - iface->iter_n_children = tree_model_n_children; - iface->iter_nth_child = tree_model_nth_child; - iface->iter_parent = tree_model_iter_parent; -} - -gint -text_import_model_iter_to_row (const GtkTreeIter *iter) -{ - assert (iter->stamp == TREE_MODEL_STAMP); - return GPOINTER_TO_INT (iter->user_data); -} - /* Increments the "watch cursor" level, setting the cursor for the assistant window to a watch face to indicate to the user that the ongoing operation may take some time. */