X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fgui%2Ftext-data-import-dialog.c;h=6f33ff6d8bb8b6547a76a91bc06d35260a3a1881;hb=ff59ee87992b440aab8083ee041f9aecd2ce68ca;hp=7598ba98ec79b0792574765a668112ae0e37dc15;hpb=1704b725af8798d00a87b7a4bed86fde902462e3;p=pspp-builds.git diff --git a/src/ui/gui/text-data-import-dialog.c b/src/ui/gui/text-data-import-dialog.c index 7598ba98..6f33ff6d 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 Free Software Foundation + Copyright (C) 2008, 2009 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,12 +16,16 @@ #include +#include + + + #include "checkbox-treeview.h" #include "descriptives-dialog.h" #include -#include -#include + +#include #include #include #include @@ -35,13 +39,13 @@ #include #include #include -#include +#include #include #include #include #include #include -#include +#include "executor.h" #include "error.h" #include "xalloc.h" @@ -50,6 +54,7 @@ #define _(msgid) gettext (msgid) #define N_(msgid) msgid + /* TextImportModel, a GtkTreeModel used by the text data import dialog. */ enum @@ -83,12 +88,13 @@ static void destroy_file (struct import_assistant *); /* The main body of the GTK+ assistant and related data. */ struct assistant { - GladeXML *xml; + GtkBuilder *builder; GtkAssistant *assistant; GMainLoop *main_loop; GtkWidget *paste_button; GtkWidget *reset_button; int response; + int watch_cursor; GtkCellRenderer *prop_renderer; GtkCellRenderer *fixed_renderer; @@ -222,13 +228,13 @@ static GtkTreeViewColumn *make_data_column (struct import_assistant *, static GtkTreeView *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, gpointer de_) +text_data_import_assistant (GObject *o, GtkWindow *parent_window) { - struct data_editor *de = de_; - GtkWindow *parent_window = de->parent.window; struct import_assistant *ia; ia = xzalloc (sizeof *ia); @@ -262,9 +268,7 @@ text_data_import_assistant (GObject *o, gpointer de_) case PSPPIRE_RESPONSE_PASTE: { char *syntax = generate_syntax (ia); - struct syntax_editor *se = - (struct syntax_editor *) window_create (WINDOW_SYNTAX, NULL); - gtk_text_buffer_insert_at_cursor (se->buffer, syntax, -1); + paste_syntax_in_new_window (syntax); free (syntax); } break; @@ -306,11 +310,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)) @@ -326,18 +328,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)); + syntax_gen_string (s, ss_cstr (val_lab_get_label (vl))); } + free (labels); ds_put_cstr (s, ".\n"); } if (var_has_label (var)) @@ -571,7 +575,7 @@ init_assistant (struct import_assistant *ia, GtkWindow *parent_window) { struct assistant *a = &ia->asst; - a->xml = XML_NEW ("text-data-import.glade"); + a->builder = builder_new ("text-data-import.ui"); a->assistant = GTK_ASSISTANT (gtk_assistant_new ()); g_signal_connect (a->assistant, "prepare", G_CALLBACK (on_prepare), ia); g_signal_connect (a->assistant, "cancel", G_CALLBACK (on_cancel), ia); @@ -585,6 +589,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"); a->prop_renderer = gtk_cell_renderer_text_new (); g_object_ref_sink (a->prop_renderer); @@ -603,7 +608,7 @@ destroy_assistant (struct import_assistant *ia) g_object_unref (a->prop_renderer); g_object_unref (a->fixed_renderer); - g_object_unref (a->xml); + g_object_unref (a->builder); } /* Appends a page of the given TYPE, with PAGE as its content, to @@ -630,6 +635,7 @@ add_page_to_assistant (struct import_assistant *ia, gtk_assistant_append_page (ia->asst.assistant, content); gtk_assistant_set_page_type (ia->asst.assistant, content, type); gtk_assistant_set_page_title (ia->asst.assistant, content, title_copy); + gtk_assistant_set_page_complete (ia->asst.assistant, content, true); free (title_copy); @@ -643,6 +649,13 @@ static void on_prepare (GtkAssistant *assistant, GtkWidget *page, struct import_assistant *ia) { + + if (gtk_assistant_get_page_type (assistant, page) + == GTK_ASSISTANT_PAGE_CONFIRM) + gtk_widget_grab_focus (assistant->apply); + else + gtk_widget_grab_focus (assistant->forward); + if (page == ia->separators.page) prepare_separators_page (ia); else if (page == ia->formats.page) @@ -653,11 +666,6 @@ on_prepare (GtkAssistant *assistant, GtkWidget *page, gtk_widget_show (ia->asst.paste_button); else gtk_widget_hide (ia->asst.paste_button); - - /* Make the user visit each page in the assistant once. Seems - like a reasonable user interface, plus visiting the final - page is what constructs the dictionary. */ - gtk_assistant_set_page_complete (assistant, page, true); } /* Called when the Cancel button in the assistant is clicked. */ @@ -719,17 +727,17 @@ static void on_intro_amount_changed (GtkToggleButton *button, static void init_intro_page (struct import_assistant *ia) { - GladeXML *xml = ia->asst.xml; + GtkBuilder *builder = ia->asst.builder; struct intro_page *p = &ia->intro; struct string s; - p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Intro"), + p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Intro"), GTK_ASSISTANT_PAGE_INTRO); - p->all_cases_button = get_widget_assert (xml, "import-all-cases"); - p->n_cases_button = get_widget_assert (xml, "import-n-cases"); - p->n_cases_spin = get_widget_assert (xml, "n-cases-spin"); - p->percent_button = get_widget_assert (xml, "import-percent"); - p->percent_spin = get_widget_assert (xml, "percent-spin"); + 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_CALLBACK (on_intro_amount_changed), ia); g_signal_connect (p->n_cases_button, "toggled", @@ -767,7 +775,7 @@ init_intro_page (struct import_assistant *ia) } ds_put_cstr (&s, _("You may choose below how much of the file should " "actually be imported.")); - gtk_label_set_text (GTK_LABEL (get_widget_assert (xml, "intro-label")), + gtk_label_set_text (GTK_LABEL (get_widget_assert (builder, "intro-label")), ds_cstr (&s)); ds_destroy (&s); } @@ -812,14 +820,14 @@ static void init_first_line_page (struct import_assistant *ia) { struct first_line_page *p = &ia->first_line; - GladeXML *xml = ia->asst.xml; + GtkBuilder *builder = ia->asst.builder; - p->page = add_page_to_assistant (ia, get_widget_assert (xml, "FirstLine"), + p->page = add_page_to_assistant (ia, get_widget_assert (builder, "FirstLine"), GTK_ASSISTANT_PAGE_CONTENT); - gtk_widget_destroy (get_widget_assert (xml, "first-line")); + gtk_widget_destroy (get_widget_assert (builder, "first-line")); p->tree_view = create_lines_tree_view ( - GTK_CONTAINER (get_widget_assert (xml, "first-line-scroller")), ia); - p->variable_names_cb = get_widget_assert (xml, "variable-names"); + 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); @@ -993,37 +1001,63 @@ static const struct separator separators[] = }; #define SEPARATOR_CNT (sizeof separators / sizeof *separators) +static void +set_quote_list (GtkComboBoxEntry *cb) +{ + GtkListStore *list = gtk_list_store_new (1, G_TYPE_STRING); + GtkTreeIter iter; + gint i; + const gchar *seperator[3] = {"'\"", "\'", "\""}; + + for (i = 0; i < 3; i++) + { + const gchar *s = seperator[i]; + + /* Add a new row to the model */ + gtk_list_store_append (list, &iter); + gtk_list_store_set (list, &iter, + 0, s, + -1); + + } + + gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list)); + + gtk_combo_box_entry_set_text_column (cb, 0); +} + /* Initializes IA's separators substructure. */ static void init_separators_page (struct import_assistant *ia) { - GladeXML *xml = ia->asst.xml; + GtkBuilder *builder = ia->asst.builder; struct separators_page *p = &ia->separators; size_t i; choose_likely_separators (ia); - p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Separators"), + p->page = add_page_to_assistant (ia, get_widget_assert (builder, "Separators"), GTK_ASSISTANT_PAGE_CONTENT); - p->custom_cb = get_widget_assert (xml, "custom-cb"); - p->custom_entry = get_widget_assert (xml, "custom-entry"); - p->quote_combo = get_widget_assert (xml, "quote-combo"); + p->custom_cb = get_widget_assert (builder, "custom-cb"); + p->custom_entry = get_widget_assert (builder, "custom-entry"); + p->quote_combo = get_widget_assert (builder, "quote-combo"); p->quote_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (p->quote_combo))); - p->quote_cb = get_widget_assert (xml, "quote-cb"); - p->escape_cb = get_widget_assert (xml, "escape"); + p->quote_cb = get_widget_assert (builder, "quote-cb"); + p->escape_cb = get_widget_assert (builder, "escape"); set_separators (ia); - p->fields_tree_view = GTK_TREE_VIEW (get_widget_assert (xml, "fields")); - g_signal_connect (GTK_COMBO_BOX (p->quote_combo), "changed", + set_quote_list (GTK_COMBO_BOX_ENTRY (p->quote_combo)); + p->fields_tree_view = GTK_TREE_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", G_CALLBACK (on_quote_cb_toggle), ia); - g_signal_connect (GTK_ENTRY (p->custom_entry), "notify::text", + g_signal_connect (p->custom_entry, "notify::text", G_CALLBACK (on_separators_custom_entry_notify), ia); g_signal_connect (p->custom_cb, "toggled", G_CALLBACK (on_separators_custom_cb_toggle), ia); for (i = 0; i < SEPARATOR_CNT; i++) - g_signal_connect (get_widget_assert (xml, separators[i].name), + g_signal_connect (get_widget_assert (builder, separators[i].name), "toggled", G_CALLBACK (on_separator_toggle), ia); g_signal_connect (p->escape_cb, "toggled", G_CALLBACK (on_separator_toggle), ia); @@ -1282,6 +1316,9 @@ static void revise_fields_preview (struct import_assistant *ia) { GtkWidget *w; + + push_watch_cursor (ia); + w = GTK_WIDGET (ia->separators.fields_tree_view); gtk_widget_destroy (w); get_separators (ia); @@ -1289,8 +1326,10 @@ revise_fields_preview (struct import_assistant *ia) choose_column_names (ia); ia->separators.fields_tree_view = create_data_tree_view ( true, - GTK_CONTAINER (get_widget_assert (ia->asst.xml, "fields-scroller")), + GTK_CONTAINER (get_widget_assert (ia->asst.builder, "fields-scroller")), ia); + + pop_watch_cursor (ia); } /* Sets the widgets to match IA's separators substructure. */ @@ -1328,7 +1367,7 @@ set_separators (struct import_assistant *ia) for (i = 0; i < SEPARATOR_CNT; i++) { const struct separator *s = &separators[i]; - GtkWidget *button = get_widget_assert (ia->asst.xml, s->name); + GtkWidget *button = get_widget_assert (ia->asst.builder, s->name); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), (seps & (1u << i)) != 0); } @@ -1340,6 +1379,7 @@ set_separators (struct import_assistant *ia) ds_destroy (&custom); any_quotes = !ds_is_empty (&s->quotes); + gtk_entry_set_text (s->quote_entry, any_quotes ? ds_cstr (&s->quotes) : "\""); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (s->quote_cb), @@ -1361,7 +1401,7 @@ get_separators (struct import_assistant *ia) for (i = 0; i < SEPARATOR_CNT; i++) { const struct separator *sep = &separators[i]; - GtkWidget *button = get_widget_assert (ia->asst.xml, sep->name); + 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); } @@ -1495,14 +1535,15 @@ static void clear_modified_vars (struct import_assistant *); static void init_formats_page (struct import_assistant *ia) { - GladeXML *xml = ia->asst.xml; + GtkBuilder *builder = ia->asst.builder; struct formats_page *p = &ia->formats; - p->page = add_page_to_assistant (ia, get_widget_assert (xml, "Formats"), + 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 (xml, "data")); + p->data_tree_view = GTK_TREE_VIEW (get_widget_assert (builder, "data")); p->modified_vars = NULL; p->modified_var_cnt = 0; + p->dict = NULL; } /* Frees IA's formats substructure. */ @@ -1536,6 +1577,8 @@ prepare_formats_page (struct import_assistant *ia) unsigned long int number = 0; size_t column_idx; + push_watch_cursor (ia); + dict = dict_create (); fg = fmt_guesser_create (); for (column_idx = 0; column_idx < s->column_cnt; column_idx++) @@ -1589,17 +1632,15 @@ prepare_formats_page (struct import_assistant *ia) hold a reference via ia->formats.dict. */ var_store = psppire_var_store_new (psppire_dict); g_object_set (var_store, - "trailing-rows", 1, "format-type", PSPPIRE_VAR_STORE_INPUT_FORMATS, (void *) NULL); var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ()); g_object_set (var_sheet, - "row-geometry", var_store, "model", var_store, "may-create-vars", FALSE, (void *) NULL); - vars_scroller = GTK_BIN (get_widget_assert (ia->asst.xml, "vars-scroller")); + vars_scroller = GTK_BIN (get_widget_assert (ia->asst.builder, "vars-scroller")); old_var_sheet = gtk_bin_get_child (vars_scroller); if (old_var_sheet != NULL) gtk_widget_destroy (old_var_sheet); @@ -1609,8 +1650,10 @@ prepare_formats_page (struct import_assistant *ia) gtk_widget_destroy (GTK_WIDGET (ia->formats.data_tree_view)); ia->formats.data_tree_view = create_data_tree_view ( false, - GTK_CONTAINER (get_widget_assert (ia->asst.xml, "data-scroller")), + GTK_CONTAINER (get_widget_assert (ia->asst.builder, "data-scroller")), ia); + + pop_watch_cursor (ia); } /* Clears the set of user-modified variables from IA's formats @@ -1649,6 +1692,8 @@ on_variable_change (PsppireDict *dict, int dict_idx, GtkTreeView *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), @@ -1671,6 +1716,8 @@ on_variable_change (PsppireDict *dict, int dict_idx, var_destroy (p->modified_vars[dict_idx]); p->modified_vars[dict_idx] = var_clone (psppire_dict_get_variable (dict, dict_idx)); + + pop_watch_cursor (ia); } /* Parses the contents of the field at (ROW,COLUMN) according to @@ -1686,7 +1733,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; @@ -1695,15 +1742,17 @@ 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))) + ia->formats.dict, + &val, var_get_width (var))) { char fmt_string[FMT_STRING_LEN_MAX + 1]; fmt_to_string (in, fmt_string); @@ -1718,16 +1767,13 @@ parse_field (struct import_assistant *ia, { 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) @@ -2242,3 +2288,32 @@ 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. */ +static void +push_watch_cursor (struct import_assistant *ia) +{ + if (++ia->asst.watch_cursor == 1) + { + GtkWidget *widget = GTK_WIDGET (ia->asst.assistant); + GdkDisplay *display = gtk_widget_get_display (widget); + GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_WATCH); + gdk_window_set_cursor (widget->window, cursor); + gdk_cursor_unref (cursor); + gdk_display_flush (display); + } +} + +/* Decrements the "watch cursor" level. If the level reaches + zero, the cursor is reset to its default shape. */ +static void +pop_watch_cursor (struct import_assistant *ia) +{ + if (--ia->asst.watch_cursor == 0) + { + GtkWidget *widget = GTK_WIDGET (ia->asst.assistant); + gdk_window_set_cursor (widget->window, NULL); + } +}