/* PSPPIRE - a graphical user interface for PSPP. Copyright (C) 2015, 2016, 2017, 2018, 2020 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 the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "psppire-import-assistant.h" #include "psppire-import-spreadsheet.h" #include "builder-wrapper.h" #include "libpspp/assertion.h" #include "libpspp/misc.h" #include "psppire-spreadsheet-model.h" #include "psppire-spreadsheet-data-model.h" #include "psppire-data-store.h" #include #define _(msgid) gettext (msgid) #define N_(msgid) msgid static void set_column_header_label (GtkWidget *button, int i, gpointer user_data) { char x[F26ADIC_STRLEN_MAX + 1]; str_format_26adic (i + 1, true, x, sizeof x); gtk_button_set_label (GTK_BUTTON (button), x); } static void do_selection_update (PsppireImportAssistant *ia); static void on_sheet_combo_changed (GtkComboBox *cb, PsppireImportAssistant *ia) { GtkBuilder *builder = ia->spread_builder; gint sheet_number = gtk_combo_box_get_active (cb); gint coli = spreadsheet_get_sheet_n_columns (ia->spreadsheet, sheet_number) - 1; gint rowi = spreadsheet_get_sheet_n_rows (ia->spreadsheet, sheet_number) - 1; { /* Now set the spin button upper limits according to the size of the selected sheet. */ GtkWidget *sb0 = get_widget_assert (builder, "sb0"); GtkWidget *sb1 = get_widget_assert (builder, "sb1"); GtkWidget *sb2 = get_widget_assert (builder, "sb2"); GtkWidget *sb3 = get_widget_assert (builder, "sb3"); /* The row spinbuttons contain decimal digits. So there should be enough space to display them. */ int digits = (rowi> 0) ? intlog10 (rowi + 1): 1; gtk_entry_set_max_width_chars (GTK_ENTRY (sb1), digits); gtk_entry_set_max_width_chars (GTK_ENTRY (sb3), digits); /* The column spinbuttons are pseudo-base-26 digits. The exact formula for the number required is complicated. However 3 is a reasonable amount. It's not too large, and anyone importing a spreadsheet with more than 3^26 columns is likely to experience other problems anyway. */ gtk_entry_set_max_width_chars (GTK_ENTRY (sb0), 3); gtk_entry_set_max_width_chars (GTK_ENTRY (sb2), 3); GtkAdjustment *adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (sb0)); gtk_adjustment_set_upper (adj, coli); adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (sb1)); gtk_adjustment_set_upper (adj, rowi); adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (sb2)); gtk_adjustment_set_upper (adj, coli); adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (sb3)); gtk_adjustment_set_upper (adj, rowi); } GtkTreeModel *data_model = psppire_spreadsheet_data_model_new (ia->spreadsheet, sheet_number); g_object_set (ia->preview_sheet, "data-model", data_model, "editable", FALSE, NULL); g_object_unref (data_model); GObject *hmodel = NULL; g_object_get (ia->preview_sheet, "hmodel", &hmodel, NULL); g_object_set (hmodel, "post-button-create-func", set_column_header_label, NULL); ia->selection.start_x = ia->selection.start_y = 0; ia->selection.end_x = coli; ia->selection.end_y = rowi; do_selection_update (ia); } /* Ensure that PARTNER is never less than than SUBJECT. */ static void on_value_change_lower (GtkSpinButton *subject, GtkSpinButton *partner) { gint p = gtk_spin_button_get_value_as_int (partner); gint s = gtk_spin_button_get_value_as_int (subject); if (s> p) gtk_spin_button_set_value (partner, s); } /* Ensure that PARTNER is never greater than to SUBJECT. */ static void on_value_change_upper (GtkSpinButton *subject, GtkSpinButton *partner) { gint p = gtk_spin_button_get_value_as_int (partner); gint s = gtk_spin_button_get_value_as_int (subject); if (s < p) gtk_spin_button_set_value (partner, s); } /* Sets SB to use 1 based display instead of 0 based. */ static gboolean row_output (GtkSpinButton *sb, gpointer unused) { gint value = gtk_spin_button_get_value_as_int (sb); char *text = g_strdup_printf ("%d", value + 1); gtk_entry_set_text (GTK_ENTRY (sb), text); free (text); return TRUE; } /* Sets SB to use text like A, B, C instead of 0, 1, 2 etc. */ static gboolean column_output (GtkSpinButton *sb, gpointer unused) { gint value = gtk_spin_button_get_value_as_int (sb); if (value < 0) return FALSE; char text[F26ADIC_STRLEN_MAX + 1]; str_format_26adic (value + 1, true, text, sizeof text); gtk_entry_set_text (GTK_ENTRY (sb), text); return TRUE; } /* Interprets the SBs text as 1 based instead of zero based. */ static gint row_input (GtkSpinButton *sb, gdouble *new_value, gpointer unused) { const char *text = gtk_entry_get_text (GTK_ENTRY (sb)); gdouble value = g_strtod (text, NULL) - 1; if (value < 0) return FALSE; *new_value = value; return TRUE; } /* Interprets the SBs text of the form A, B, C etc and sets NEW_VALUE as a double. */ static gint column_input (GtkSpinButton *sb, gdouble *new_value, gpointer unused) { const char *text = gtk_entry_get_text (GTK_ENTRY (sb)); double value = str_parse_26adic (text); if (value < 0) return FALSE; *new_value = value; return TRUE; } static void reset_page (PsppireImportAssistant *ia) { GtkBuilder *builder = ia->spread_builder; GtkWidget *readnames_checkbox = get_widget_assert (builder, "readnames-checkbox"); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (readnames_checkbox), FALSE); gint sheet_number = 0; GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry"); gtk_combo_box_set_active (GTK_COMBO_BOX (sheet_entry), sheet_number); gint coli = spreadsheet_get_sheet_n_columns (ia->spreadsheet, sheet_number) - 1; gint rowi = spreadsheet_get_sheet_n_rows (ia->spreadsheet, sheet_number) - 1; ia->selection.start_x = ia->selection.start_y = 0; ia->selection.end_x = coli; ia->selection.end_y = rowi; do_selection_update (ia); } /* Prepares IA's sheet_spec page. */ static void prepare_sheet_spec_page (PsppireImportAssistant *ia, GtkWidget *page, enum IMPORT_ASSISTANT_DIRECTION dir) { if (dir != IMPORT_ASSISTANT_FORWARDS) return; GtkBuilder *builder = ia->spread_builder; GtkWidget *sheet_entry = get_widget_assert (builder, "sheet-entry"); GtkWidget *readnames_checkbox = get_widget_assert (builder, "readnames-checkbox"); GtkTreeModel *model = psppire_spreadsheet_model_new (ia->spreadsheet); gtk_combo_box_set_model (GTK_COMBO_BOX (sheet_entry), model); g_object_unref (model); gint items = gtk_tree_model_iter_n_children (model, NULL); gtk_widget_set_sensitive (sheet_entry, items> 1); gtk_combo_box_set_active (GTK_COMBO_BOX (sheet_entry), 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (readnames_checkbox), FALSE); GtkWidget *file_name_label = get_widget_assert (builder, "file-name-label"); gtk_label_set_text (GTK_LABEL (file_name_label), ia->file_name); /* Gang the increment/decrement buttons, so that the upper always exceeds the lower. */ GtkWidget *sb0 = get_widget_assert (builder, "sb0"); GtkWidget *sb2 = get_widget_assert (builder, "sb2"); g_signal_connect (sb0, "value-changed", G_CALLBACK (on_value_change_lower), sb2); g_signal_connect (sb2, "value-changed", G_CALLBACK (on_value_change_upper), sb0); GtkWidget *sb1 = get_widget_assert (builder, "sb1"); GtkWidget *sb3 = get_widget_assert (builder, "sb3"); g_signal_connect (sb1, "value-changed", G_CALLBACK (on_value_change_lower), sb3); g_signal_connect (sb3, "value-changed", G_CALLBACK (on_value_change_upper), sb1); /* Set the column spinbuttons to display as A, B, C notation, and the row spinbuttons to display as 1 based instead of zero based. */ g_signal_connect (sb0, "output", G_CALLBACK (column_output), NULL); g_signal_connect (sb0, "input", G_CALLBACK (column_input), NULL); g_signal_connect (sb2, "output", G_CALLBACK (column_output), NULL); g_signal_connect (sb2, "input", G_CALLBACK (column_input), NULL); g_signal_connect (sb1, "output", G_CALLBACK (row_output), NULL); g_signal_connect (sb1, "input", G_CALLBACK (row_input), NULL); g_signal_connect (sb3, "output", G_CALLBACK (row_output), NULL); g_signal_connect (sb3, "input", G_CALLBACK (row_input), NULL); } static void do_selection_update (PsppireImportAssistant *ia) { GtkBuilder *builder = ia->spread_builder; /* Stop this function re-entering itself. */ if (ia->updating_selection) return; ia->updating_selection = TRUE; /* We must take a copy of the selection. A pointer will not suffice, because the selection can change under us. */ SswRange sel = ia->selection; g_object_set (ia->preview_sheet, "selection", &sel, NULL); char *range = create_cell_range (sel.start_x, sel.start_y, sel.end_x, sel.end_y); GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry"); if (range) gtk_entry_set_text (GTK_ENTRY (range_entry), range); free (range); GtkWidget *sb0 = get_widget_assert (builder, "sb0"); GtkWidget *sb1 = get_widget_assert (builder, "sb1"); GtkWidget *sb2 = get_widget_assert (builder, "sb2"); GtkWidget *sb3 = get_widget_assert (builder, "sb3"); gtk_spin_button_set_value (GTK_SPIN_BUTTON (sb0), sel.start_x); gtk_spin_button_set_value (GTK_SPIN_BUTTON (sb1), sel.start_y); gtk_spin_button_set_value (GTK_SPIN_BUTTON (sb2), sel.end_x); gtk_spin_button_set_value (GTK_SPIN_BUTTON (sb3), sel.end_y); ia->updating_selection = FALSE; } static void on_preview_selection_changed (SswSheet *sheet, gpointer selection, PsppireImportAssistant *ia) { memcpy (&ia->selection, selection, sizeof (ia->selection)); do_selection_update (ia); } static void entry_update_selected_range (GtkEntry *entry, PsppireImportAssistant *ia) { const char *text = gtk_entry_get_text (entry); if (convert_cell_ref (text, &ia->selection.start_x, &ia->selection.start_y, &ia->selection.end_x, &ia->selection.end_y)) { do_selection_update (ia); } } /* On change of any spinbutton, update the selected range accordingly. */ static void sb_update_selected_range (PsppireImportAssistant *ia) { GtkBuilder *builder = ia->spread_builder; GtkWidget *sb0 = get_widget_assert (builder, "sb0"); GtkWidget *sb1 = get_widget_assert (builder, "sb1"); GtkWidget *sb2 = get_widget_assert (builder, "sb2"); GtkWidget *sb3 = get_widget_assert (builder, "sb3"); ia->selection.start_x = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sb0)); ia->selection.start_y = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sb1)); ia->selection.end_x = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sb2)); ia->selection.end_y = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (sb3)); do_selection_update (ia); } /* Initializes IA's sheet_spec substructure. */ void sheet_spec_page_create (PsppireImportAssistant *ia) { GtkBuilder *builder = ia->spread_builder; GtkWidget *page = get_widget_assert (builder, "Spreadsheet-Importer"); ia->preview_sheet = get_widget_assert (builder, "preview-sheet"); g_signal_connect (ia->preview_sheet, "selection-changed", G_CALLBACK (on_preview_selection_changed), ia); gtk_widget_show (ia->preview_sheet); { GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry"); GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box)); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, "text", 0, NULL); g_signal_connect (combo_box, "changed", G_CALLBACK (on_sheet_combo_changed), ia); } { GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry"); g_signal_connect (range_entry, "changed", G_CALLBACK (entry_update_selected_range), ia); GtkWidget *sb0 = get_widget_assert (builder, "sb0"); g_signal_connect_swapped (sb0, "value-changed", G_CALLBACK (sb_update_selected_range), ia); GtkWidget *sb1 = get_widget_assert (builder, "sb1"); g_signal_connect_swapped (sb1, "value-changed", G_CALLBACK (sb_update_selected_range), ia); GtkWidget *sb2 = get_widget_assert (builder, "sb2"); g_signal_connect_swapped (sb2, "value-changed", G_CALLBACK (sb_update_selected_range), ia); GtkWidget *sb3 = get_widget_assert (builder, "sb3"); g_signal_connect_swapped (sb3, "value-changed", G_CALLBACK (sb_update_selected_range), ia); } add_page_to_assistant (ia, page, GTK_ASSISTANT_PAGE_CONTENT, _("Importing Spreadsheet Data")); g_object_set_data (G_OBJECT (page), "on-entering", prepare_sheet_spec_page); g_object_set_data (G_OBJECT (page), "on-reset", reset_page); } /* Set the data model for both the data sheet and the variable sheet. */ void spreadsheet_set_data_models (PsppireImportAssistant *ia) { GtkBuilder *builder = ia->spread_builder; GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry"); GtkWidget *rnc = get_widget_assert (builder, "readnames-checkbox"); GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry"); struct spreadsheet_read_options opts; opts.sheet_name = NULL; opts.sheet_index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) + 1; opts.read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rnc)); opts.cell_range = g_strdup (gtk_entry_get_text (GTK_ENTRY (range_entry))); opts.asw = 8; struct casereader *reader = spreadsheet_make_reader (ia->spreadsheet, &opts); PsppireDict *dict = psppire_dict_new_from_dict (ia->spreadsheet->dict); PsppireDataStore *store = psppire_data_store_new (dict); psppire_data_store_set_reader (store, reader); g_object_set (ia->data_sheet, "data-model", store, NULL); g_object_set (ia->var_sheet, "data-model", dict, NULL); } AltStyle によって変換されたページ (->オリジナル) / アドレス: モード: デフォルト 音声ブラウザ ルビ付き 配色反転 文字拡大 モバイル
AltStyle によって変換されたページ (->オリジナル) / アドレス: モード: デフォルト 音声ブラウザ ルビ付き 配色反転 文字拡大 モバイル