From: John Darrington Date: Mon, 1 Feb 2016 20:21:29 +0000 (+0100) Subject: Adapt to new sheet X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aa6f8e301fab021ba2fea720a162e506612ba29f;p=pspp Adapt to new sheet --- diff --git a/configure.ac b/configure.ac index 9ce7699833..b8c84ea33d 100644 --- a/configure.ac +++ b/configure.ac @@ -90,8 +90,8 @@ if test "$with_cairo" != no && test "$with_gui" != "no"; then PKG_CHECK_MODULES([GTKSOURCEVIEW], [gtksourceview-3.0 >= 3.4.2], [], [PSPP_REQUIRED_PREREQ([gtksourceview 3.0 version 3.4.2 or later (or use --without-gui)])]) - PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.32], [], - [PSPP_REQUIRED_PREREQ([glib 2.0 version 2.32 or later (or use --without-gui)])]) + PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.44], [], + [PSPP_REQUIRED_PREREQ([glib 2.0 version 2.44 or later (or use --without-gui)])]) AC_ARG_VAR([GLIB_GENMARSHAL]) AC_CHECK_PROGS([GLIB_GENMARSHAL], [glib-genmarshal]) diff --git a/lib/gtk-contrib/gtkxpaned.c b/lib/gtk-contrib/gtkxpaned.c index 9be83311f0..19dea4554b 100644 --- a/lib/gtk-contrib/gtkxpaned.c +++ b/lib/gtk-contrib/gtkxpaned.c @@ -54,7 +54,9 @@ enum ChildProperties { CHILD_PROP_0, CHILD_PROP_RESIZE, - CHILD_PROP_SHRINK + CHILD_PROP_SHRINK, + CHILD_PROP_LEFT_ATTACH, + CHILD_PROP_TOP_ATTACH }; enum WidgetSignals @@ -526,6 +528,29 @@ gtk_xpaned_class_init (GtkXPanedClass * class) G_MAXINT, G_PARAM_READABLE)); + + gtk_container_class_install_child_property (container_class, + CHILD_PROP_LEFT_ATTACH, + g_param_spec_int ("left-attach", + "Left Attach", + "The column number to which the left side of the widget should be attached", + 0, 1, + 0, + G_PARAM_READWRITE)); + + + + gtk_container_class_install_child_property (container_class, + CHILD_PROP_TOP_ATTACH, + g_param_spec_int ("top-attach", + "Top Attach", + "The row number to which the top side of the widget should be attached", + 0, 1, + 0, + G_PARAM_READWRITE)); + + + /** * GtkPaned:resize: * @@ -812,6 +837,12 @@ gtk_xpaned_size_allocate (GtkWidget * widget, GtkAllocation * allocation) GtkRequisition bottom_right_child_requisition; gint handle_size; + g_print ("Allocate %p %p %p %p\n", + xpaned->top_left_child, + xpaned->top_right_child, + xpaned->bottom_left_child, + xpaned->bottom_right_child); + /* determine size of handle(s) */ gtk_widget_style_get (widget, "handle-size", &handle_size, NULL); @@ -1046,73 +1077,109 @@ gtk_xpaned_set_child_property (GtkContainer * container, const GValue * value, GParamSpec * pspec) { GtkXPaned *xpaned = GTK_XPANED (container); - gboolean old_value = FALSE; - gboolean new_value = FALSE; g_assert (child == xpaned->top_left_child || child == xpaned->top_right_child || child == xpaned->bottom_left_child || child == xpaned->bottom_right_child); - new_value = g_value_get_boolean (value); - + gint attach = g_value_get_int (value); switch (property_id) { + case CHILD_PROP_LEFT_ATTACH: + g_object_ref (child); + gtk_widget_unparent (child); + if (attach == 0) + { + if (child == xpaned->top_right_child) + xpaned->top_left_child = child; + else if (child == xpaned->bottom_right_child) + xpaned->bottom_left_child = child; + } + else + { + if (child == xpaned->top_left_child) + xpaned->top_right_child = child; + else if (child == xpaned->bottom_left_child) + xpaned->bottom_right_child = child; + } + gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); + g_object_unref (child); + break; + case CHILD_PROP_TOP_ATTACH: + g_object_ref (child); + gtk_widget_unparent (child); + if (attach == 0) + { + if (child == xpaned->bottom_right_child) + xpaned->top_right_child = child; + else if (child == xpaned->bottom_left_child) + xpaned->top_left_child = child; + } + else + { + if (child == xpaned->top_left_child) + xpaned->bottom_left_child = child; + else if (child == xpaned->top_right_child) + xpaned->bottom_right_child = child; + } + gtk_widget_set_parent (child, GTK_WIDGET (xpaned)); + g_object_unref (child); + break; case CHILD_PROP_RESIZE: - if (child == xpaned->top_left_child) - { - old_value = xpaned->top_left_child_resize; - xpaned->top_left_child_resize = new_value; - } - else if (child == xpaned->top_right_child) - { - old_value = xpaned->top_right_child_resize; - xpaned->top_right_child_resize = new_value; - } - else if (child == xpaned->bottom_left_child) - { - old_value = xpaned->bottom_left_child_resize; - xpaned->bottom_left_child_resize = new_value; - } - else if (child == xpaned->bottom_right_child) - { - old_value = xpaned->bottom_right_child_resize; - xpaned->bottom_right_child_resize = new_value; - } + { + gboolean new_value = TRUE; + + if (child == xpaned->top_left_child) + { + xpaned->top_left_child_resize = new_value; + } + else if (child == xpaned->top_right_child) + { + xpaned->top_right_child_resize = new_value; + } + else if (child == xpaned->bottom_left_child) + { + xpaned->bottom_left_child_resize = new_value; + } + else if (child == xpaned->bottom_right_child) + { + xpaned->bottom_right_child_resize = new_value; + } + } break; - + case CHILD_PROP_SHRINK: - if (child == xpaned->top_left_child) - { - old_value = xpaned->top_left_child_shrink; - xpaned->top_left_child_shrink = new_value; - } - else if (child == xpaned->top_right_child) - { - old_value = xpaned->top_right_child_shrink; - xpaned->top_right_child_shrink = new_value; - } - else if (child == xpaned->bottom_left_child) - { - old_value = xpaned->bottom_left_child_shrink; - xpaned->bottom_left_child_shrink = new_value; - } - else if (child == xpaned->bottom_right_child) - { - old_value = xpaned->bottom_right_child_shrink; - xpaned->bottom_right_child_shrink = new_value; - } + { + gboolean new_value = FALSE; + + if (child == xpaned->top_left_child) + { + xpaned->top_left_child_shrink = new_value; + } + else if (child == xpaned->top_right_child) + { + xpaned->top_right_child_shrink = new_value; + } + else if (child == xpaned->bottom_left_child) + { + xpaned->bottom_left_child_shrink = new_value; + } + else if (child == xpaned->bottom_right_child) + { + xpaned->bottom_right_child_shrink = new_value; + } + } break; default: GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); - old_value = -1; /* quiet gcc */ break; } - if (old_value != new_value) - gtk_widget_queue_resize (GTK_WIDGET (container)); + gtk_widget_queue_resize (GTK_WIDGET (container)); + gtk_widget_queue_draw (GTK_WIDGET (container)); } static void @@ -1130,6 +1197,26 @@ gtk_xpaned_get_child_property (GtkContainer * container, switch (property_id) { + case CHILD_PROP_TOP_ATTACH: + if (child == xpaned->top_left_child) + g_value_set_int (value, 0); + if (child == xpaned->top_right_child) + g_value_set_int (value, 0); + if (child == xpaned->bottom_left_child) + g_value_set_int (value, 1); + if (child == xpaned->bottom_right_child) + g_value_set_int (value, 1); + break; + case CHILD_PROP_LEFT_ATTACH: + if (child == xpaned->top_left_child) + g_value_set_int (value, 0); + if (child == xpaned->bottom_left_child) + g_value_set_int (value, 0); + if (child == xpaned->top_right_child) + g_value_set_int (value, 1); + if (child == xpaned->bottom_right_child) + g_value_set_int (value, 1); + break; case CHILD_PROP_RESIZE: if (child == xpaned->top_left_child) g_value_set_boolean (value, xpaned->top_left_child_resize); diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index a88e8e733d..4d0e6ff4a3 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -383,6 +383,7 @@ dist_appdata_DATA = src/ui/gui/pspp.appdata.xml BUILT_SOURCES += src/ui/gui/psppire-marshal.c src/ui/gui/psppire-marshal.h CLEANFILES += src/ui/gui/psppire-marshal.c src/ui/gui/psppire-marshal.h \ $(nodist_src_ui_gui_psppire_DATA) +include $(top_srcdir)/src/ui/gui/efficient-sheet/automake.mk endif HAVE_GUI #ensure the installcheck passes even if there is no X server available diff --git a/src/ui/gui/find-dialog.c b/src/ui/gui/find-dialog.c index fddbe8c49b..c8f8451bd8 100644 --- a/src/ui/gui/find-dialog.c +++ b/src/ui/gui/find-dialog.c @@ -36,7 +36,6 @@ which match particular strings */ #include "ui/gui/dict-display.h" #include "ui/gui/find-dialog.h" #include "ui/gui/helper.h" -#include "ui/gui/psppire-data-sheet.h" #include "ui/gui/psppire-data-store.h" #include "ui/gui/psppire-data-window.h" #include "ui/gui/psppire-dialog.h" @@ -100,13 +99,12 @@ refresh (GObject *obj, const struct find_dialog *fd) static void do_find (GObject *obj, const struct find_dialog *fd) { - PsppireDataSheet *data_sheet; casenumber x = -1; gint column = -1; glong row; - data_sheet = psppire_data_editor_get_active_data_sheet (fd->de->data_editor); - row = psppire_data_sheet_get_selected_case (data_sheet); + + row = 10; find_value (fd, row, &x, &column); @@ -115,8 +113,6 @@ do_find (GObject *obj, const struct find_dialog *fd) gtk_notebook_set_current_page (GTK_NOTEBOOK (fd->de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW); - psppire_data_sheet_goto_case (data_sheet, x); - psppire_data_sheet_goto_variable (data_sheet, column); } } diff --git a/src/ui/gui/goto-case-dialog.c b/src/ui/gui/goto-case-dialog.c index 282f416368..54d8eaf334 100644 --- a/src/ui/gui/goto-case-dialog.c +++ b/src/ui/gui/goto-case-dialog.c @@ -24,45 +24,11 @@ static void -refresh (PsppireDataSheet *ds, GtkBuilder *xml) +refresh (GtkWidget *ds, GtkBuilder *xml) { - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (ds); - casenumber case_count ; - - GtkWidget *case_num_entry = get_widget_assert (xml, "goto-case-case-num-entry"); - - case_count = psppire_data_store_get_case_count (data_store); - - gtk_spin_button_set_range (GTK_SPIN_BUTTON (case_num_entry), 1, case_count); } void -goto_case_dialog (PsppireDataSheet *ds) +goto_case_dialog (void *ds) { - GtkWindow *top_level; - gint response; - GtkBuilder *xml = builder_new ("goto-case.ui"); - GtkWidget *dialog = get_widget_assert (xml, "goto-case-dialog"); - - top_level = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (ds))); - gtk_window_set_transient_for (GTK_WINDOW (dialog), top_level); - - refresh (ds, xml); - - response = psppire_dialog_run (PSPPIRE_DIALOG (dialog)); - - if ( response == PSPPIRE_RESPONSE_GOTO ) - { - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (ds); - glong case_num; - GtkWidget *case_num_entry = - get_widget_assert (xml, "goto-case-case-num-entry"); - - case_num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (case_num_entry)) - - FIRST_CASE_NUMBER ; - - if (case_num >= 0 - && case_num < psppire_data_store_get_case_count (data_store)) - psppire_data_sheet_goto_case (ds, case_num); - } } diff --git a/src/ui/gui/goto-case-dialog.h b/src/ui/gui/goto-case-dialog.h index e7c70eee25..455e2e637c 100644 --- a/src/ui/gui/goto-case-dialog.h +++ b/src/ui/gui/goto-case-dialog.h @@ -17,8 +17,7 @@ #ifndef __GOTO_CASE_DIALOG_H #define __GOTO_CASE_DIALOG_H -#include "psppire-data-sheet.h" -void goto_case_dialog (PsppireDataSheet *ds); +void goto_case_dialog (void *ds); #endif diff --git a/src/ui/gui/psppire-data-editor.c b/src/ui/gui/psppire-data-editor.c index 18fd5e8e8d..e3152e8ca2 100644 --- a/src/ui/gui/psppire-data-editor.c +++ b/src/ui/gui/psppire-data-editor.c @@ -27,21 +27,16 @@ #include "libpspp/str.h" #include "ui/gui/helper.h" #include "ui/gui/pspp-sheet-selection.h" -#include "ui/gui/psppire-data-sheet.h" #include "ui/gui/psppire-data-store.h" #include "ui/gui/psppire-value-entry.h" #include "ui/gui/psppire-var-sheet.h" #include "ui/gui/psppire-conf.h" +#include "ui/gui/efficient-sheet/jmd-sheet.h" + #include #define _(msgid) gettext (msgid) -#define FOR_EACH_DATA_SHEET(DATA_SHEET, IDX, DATA_EDITOR) \ - for ((IDX) = 0; \ - (IDX) < 4 \ - && ((DATA_SHEET) = PSPPIRE_DATA_SHEET ( \ - (DATA_EDITOR)->data_sheets[IDX])) != NULL; \ - (IDX)++) static void psppire_data_editor_class_init (PsppireDataEditorClass *klass); static void psppire_data_editor_init (PsppireDataEditor *de); @@ -128,11 +123,8 @@ static void psppire_data_editor_refresh_model (PsppireDataEditor *de) { PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (de->var_sheet); - PsppireDataSheet *data_sheet; - int i; + int i; - FOR_EACH_DATA_SHEET (data_sheet, i, de) - psppire_data_sheet_set_data_store (data_sheet, de->data_store); psppire_var_sheet_set_dictionary (var_sheet, de->dict); } @@ -143,7 +135,7 @@ psppire_data_editor_set_property (GObject *object, GParamSpec *pspec) { PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object); - PsppireDataSheet *data_sheet; + int i; switch (prop_id) @@ -162,6 +154,9 @@ psppire_data_editor_set_property (GObject *object, de->data_store = g_value_get_pointer (value); g_object_ref (de->data_store); + g_print ("NEW STORE\n"); + + g_object_set (de->data_sheet, "data-model", de->data_store, NULL); psppire_data_editor_refresh_model (de); g_signal_connect_swapped (de->data_store, "case-changed", @@ -174,13 +169,12 @@ psppire_data_editor_set_property (GObject *object, de->dict = g_value_get_pointer (value); g_object_ref (de->dict); + g_object_set (de->data_sheet, "hmodel", de->dict, NULL); + psppire_var_sheet_set_dictionary (PSPPIRE_VAR_SHEET (de->var_sheet), de->dict); break; case PROP_VALUE_LABELS: - FOR_EACH_DATA_SHEET (data_sheet, i, de) - psppire_data_sheet_set_value_labels (data_sheet, - g_value_get_boolean (value)); break; case PROP_UI_MANAGER: default: @@ -209,9 +203,6 @@ psppire_data_editor_get_property (GObject *object, g_value_set_pointer (value, de->dict); break; case PROP_VALUE_LABELS: - g_value_set_boolean (value, - psppire_data_sheet_get_value_labels ( - PSPPIRE_DATA_SHEET (de->data_sheets[0]))); break; case PROP_UI_MANAGER: g_value_set_object (value, psppire_data_editor_get_ui_manager (de)); @@ -316,7 +307,7 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass) } static gboolean -on_data_sheet_var_double_clicked (PsppireDataSheet *data_sheet, +on_data_sheet_var_double_clicked (GtkWidget *data_sheet, gint dict_index, PsppireDataEditor *de) { @@ -333,13 +324,10 @@ static gboolean on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index, PsppireDataEditor *de) { - PsppireDataSheet *data_sheet; gtk_notebook_set_current_page (GTK_NOTEBOOK (de), PSPPIRE_DATA_EDITOR_DATA_VIEW); - data_sheet = psppire_data_editor_get_active_data_sheet (de); - psppire_data_sheet_goto_variable (data_sheet, dict_index); return TRUE; } @@ -349,134 +337,11 @@ on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index, static void refresh_entry (PsppireDataEditor *de) { - PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de); - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - - gchar *ref_cell_text; - GList *selected_columns, *iter; - struct variable *var; - gint n_cases; - gint n_vars; - - selected_columns = pspp_sheet_selection_get_selected_columns (selection); - n_vars = 0; - var = NULL; - for (iter = selected_columns; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *v = g_object_get_data (G_OBJECT (column), "variable"); - if (v != NULL) - { - var = v; - n_vars++; - } - } - g_list_free (selected_columns); - - n_cases = pspp_sheet_selection_count_selected_rows (selection); - if (n_cases > 0) - { - /* The final row is selectable but it isn't a case (it's just used to add - more cases), so don't count it. */ - GtkTreePath *path; - gint case_count; - - case_count = psppire_data_store_get_case_count (de->data_store); - path = gtk_tree_path_new_from_indices (case_count, -1); - if (pspp_sheet_selection_path_is_selected (selection, path)) - n_cases--; - gtk_tree_path_free (path); - } - - ref_cell_text = NULL; - if (n_cases == 1 && n_vars == 1) - { - PsppireValueEntry *value_entry = PSPPIRE_VALUE_ENTRY (de->datum_entry); - struct range_set *selected_rows; - gboolean show_value_labels; - union value value; - int width; - gint row; - - selected_rows = pspp_sheet_selection_get_range_set (selection); - row = range_set_scan (selected_rows, 0); - range_set_destroy (selected_rows); - - ref_cell_text = g_strdup_printf ("%d : %s", row + 1, var_get_name (var)); - - show_value_labels = psppire_data_sheet_get_value_labels (data_sheet); - - psppire_value_entry_set_variable (value_entry, var); - psppire_value_entry_set_show_value_label (value_entry, - show_value_labels); - - width = var_get_width (var); - value_init (&value, width); - datasheet_get_value (de->data_store->datasheet, - row, var_get_case_index (var), &value); - psppire_value_entry_set_value (value_entry, &value, width); - value_destroy (&value, width); - - gtk_widget_set_sensitive (de->datum_entry, TRUE); - } - else - { - if (n_cases == 0 || n_vars == 0) - { - ref_cell_text = NULL; - } - else - { - struct string s; - - /* The glib string library does not understand the ' printf modifier - on all platforms, but the "struct string" library does (because - Gnulib fixes that problem), so use the latter. */ - ds_init_empty (&s); - ds_put_format (&s, ngettext ("%'d case", "%'d cases", n_cases), - n_cases); - ds_put_byte (&s, ' '); - ds_put_unichar (&s, 0xd7); /* U+00D7 MULTIPLICATION SIGN */ - ds_put_byte (&s, ' '); - ds_put_format (&s, ngettext ("%'d variable", "%'d variables", - n_vars), - n_vars); - ref_cell_text = ds_steal_cstr (&s); - } - - psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry), - NULL); - gtk_entry_set_text ( - GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))), ""); - gtk_widget_set_sensitive (de->datum_entry, FALSE); - } - - gtk_label_set_label (GTK_LABEL (de->cell_ref_label), - ref_cell_text ? ref_cell_text : ""); - g_free (ref_cell_text); } static void on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de) { - PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de); - struct variable *var; - union value value; - int width; - gint row; - - row = psppire_data_sheet_get_current_case (data_sheet); - var = psppire_data_sheet_get_current_variable (data_sheet); - if (row < 0 || !var) - return; - - width = var_get_width (var); - value_init (&value, width); - if (psppire_value_entry_get_value (PSPPIRE_VALUE_ENTRY (de->datum_entry), - &value, width)) - psppire_data_store_set_value (de->data_store, row, var, &value); - value_destroy (&value, width); } static void @@ -489,209 +354,19 @@ on_data_sheet_selection_changed (PsppSheetSelection *selection, && pspp_sheet_selection_count_selected_rows (selection) && pspp_sheet_selection_count_selected_columns (selection)) { - PsppireDataSheet *ds; - int i; - - FOR_EACH_DATA_SHEET (ds, i, de) - { - PsppSheetSelection *s; - - s = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds)); - if (s != selection) - pspp_sheet_selection_unselect_all (s); - } } refresh_entry (de); } -/* Ensures that rows in the right-hand panes in the split view have the same - row height as the left-hand panes. Otherwise, the rows in the right-hand - pane tend to be smaller, because the right-hand pane doesn't have buttons - for case numbers. */ -static void -on_data_sheet_fixed_height_notify (PsppireDataSheet *ds, - GParamSpec *pspec, - PsppireDataEditor *de) -{ - enum - { - TL = GTK_XPANED_TOP_LEFT, - TR = GTK_XPANED_TOP_RIGHT, - BL = GTK_XPANED_BOTTOM_LEFT, - BR = GTK_XPANED_BOTTOM_RIGHT - }; - - int fixed_height = pspp_sheet_view_get_fixed_height (PSPP_SHEET_VIEW (ds)); - - pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[TR]), - fixed_height); - pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[BR]), - fixed_height); -} static void disconnect_data_sheets (PsppireDataEditor *de) { - PsppireDataSheet *ds; int i; - FOR_EACH_DATA_SHEET (ds, i, de) - { - PsppSheetSelection *selection; - - if (ds == NULL) - { - /* This can only happen if 'dispose' runs more than once. */ - continue; - } - - if (i == GTK_XPANED_TOP_LEFT) - g_signal_handlers_disconnect_by_func ( - ds, G_CALLBACK (on_data_sheet_fixed_height_notify), de); - - g_signal_handlers_disconnect_by_func ( - ds, G_CALLBACK (refresh_entry), de); - g_signal_handlers_disconnect_by_func ( - ds, G_CALLBACK (on_data_sheet_var_double_clicked), de); - - selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds)); - g_signal_handlers_disconnect_by_func ( - selection, G_CALLBACK (on_data_sheet_selection_changed), de); - - de->data_sheets[i] = NULL; - } -} - -static GtkWidget * -make_data_sheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines, - gboolean show_value_labels) -{ - PsppSheetSelection *selection; - GtkWidget *ds; - - ds = psppire_data_sheet_new (); - pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines); - psppire_data_sheet_set_value_labels (PSPPIRE_DATA_SHEET (ds), - show_value_labels); - - g_signal_connect_swapped (ds, "notify::value-labels", - G_CALLBACK (refresh_entry), de); - g_signal_connect (ds, "var-double-clicked", - G_CALLBACK (on_data_sheet_var_double_clicked), de); - - selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds)); - g_signal_connect (selection, "changed", - G_CALLBACK (on_data_sheet_selection_changed), de); - - return ds; -} - -static GtkWidget * -make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines, - gboolean show_value_labels) -{ - GtkWidget *data_sheet_scroller; - - de->data_sheets[0] = make_data_sheet (de, grid_lines, show_value_labels); - de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL; - - /* Put data sheet in scroller. */ - data_sheet_scroller = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (data_sheet_scroller), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_container_add (GTK_CONTAINER (data_sheet_scroller), de->data_sheets[0]); - - return data_sheet_scroller; } -static GtkWidget * -make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines, - gboolean show_value_labels) -{ - /* Panes, in the order in which we want to create them. */ - enum - { - TL, /* top left */ - TR, /* top right */ - BL, /* bottom left */ - BR /* bottom right */ - }; - - PsppSheetView *ds[4]; - GtkXPaned *xpaned; - int i; - - xpaned = GTK_XPANED (gtk_xpaned_new ()); - - for (i = 0; i < 4; i++) - { - GtkAdjustment *hadjust, *vadjust; - GtkPolicyType hpolicy, vpolicy; - GtkWidget *scroller; - - de->data_sheets[i] = make_data_sheet (de, grid_lines, show_value_labels); - ds[i] = PSPP_SHEET_VIEW (de->data_sheets[i]); - - if (i == BL) - hadjust = pspp_sheet_view_get_hadjustment (ds[TL]); - else if (i == BR) - hadjust = pspp_sheet_view_get_hadjustment (ds[TR]); - else - hadjust = NULL; - - if (i == TR) - vadjust = pspp_sheet_view_get_vadjustment (ds[TL]); - else if (i == BR) - vadjust = pspp_sheet_view_get_vadjustment (ds[BL]); - else - vadjust = NULL; - - scroller = gtk_scrolled_window_new (hadjust, vadjust); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller), - GTK_SHADOW_ETCHED_IN); - hpolicy = i == TL || i == TR ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS; - vpolicy = i == TL || i == BL ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS; - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller), - hpolicy, vpolicy); - gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ds[i])); - - switch (i) - { - case TL: - gtk_xpaned_pack_top_left (xpaned, scroller, TRUE, TRUE); - break; - - case TR: - gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE); - break; - - case BL: - gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE); - break; - - case BR: - gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE); - break; - - default: - g_warn_if_reached (); - } - } - - /* Bottom sheets don't display variable names. */ - pspp_sheet_view_set_headers_visible (ds[BL], FALSE); - pspp_sheet_view_set_headers_visible (ds[BR], FALSE); - - /* Right sheets don't display case numbers. */ - psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[TR]), FALSE); - psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[BR]), FALSE); - - g_signal_connect (ds[TL], "notify::fixed-height", - G_CALLBACK (on_data_sheet_fixed_height_notify), de); - - return GTK_WIDGET (xpaned); -} static void set_font_recursively (GtkWidget *w, gpointer data); @@ -721,13 +396,14 @@ psppire_data_editor_init (PsppireDataEditor *de) gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0); de->split = FALSE; - de->datasheet_vbox_widget - = make_single_datasheet (de, GTK_TREE_VIEW_GRID_LINES_BOTH, FALSE); - + de->data_sheet = g_object_new (JMD_TYPE_SHEET, + "splitter", GTK_TYPE_XPANED, + NULL); + GtkWidget *button = jmd_sheet_get_button (JMD_SHEET (de->data_sheet)); + gtk_button_set_label (GTK_BUTTON (button), _("Case")); de->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget, - TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (de->vbox), de->data_sheet, TRUE, TRUE, 0); gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox, gtk_label_new_with_mnemonic (_("Data View"))); @@ -735,6 +411,7 @@ psppire_data_editor_init (PsppireDataEditor *de) gtk_widget_show_all (de->vbox); de->var_sheet = GTK_WIDGET (psppire_var_sheet_new ()); + pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), GTK_TREE_VIEW_GRID_LINES_BOTH); var_sheet_scroller = gtk_scrolled_window_new (NULL, NULL); @@ -742,6 +419,7 @@ psppire_data_editor_init (PsppireDataEditor *de) GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add (GTK_CONTAINER (var_sheet_scroller), de->var_sheet); gtk_widget_show_all (var_sheet_scroller); + gtk_notebook_append_page (GTK_NOTEBOOK (de), var_sheet_scroller, gtk_label_new_with_mnemonic (_("Variable View"))); @@ -778,15 +456,12 @@ void psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible) { GtkTreeViewGridLines grid; - PsppireDataSheet *data_sheet; int i; grid = (grid_visible ? GTK_TREE_VIEW_GRID_LINES_BOTH : GTK_TREE_VIEW_GRID_LINES_NONE); - FOR_EACH_DATA_SHEET (data_sheet, i, de) - pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid); pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid); } @@ -832,30 +507,11 @@ psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split) return; - grid_lines = pspp_sheet_view_get_grid_lines ( - PSPP_SHEET_VIEW (de->data_sheets[0])); - labels = psppire_data_sheet_get_value_labels (PSPPIRE_DATA_SHEET ( - de->data_sheets[0])); disconnect_data_sheets (de); - if (de->old_vbox_widget) - g_object_unref (de->old_vbox_widget); - de->old_vbox_widget = de->datasheet_vbox_widget; - g_object_ref (de->old_vbox_widget); - /* FIXME: old_vbox_widget needs to be unreffed in dispose. - (currently it seems to provoke an error if I do that. - I don't know why. */ - gtk_container_remove (GTK_CONTAINER (de->vbox), de->datasheet_vbox_widget); - - if (split) - de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines, labels); - else - de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines, labels); psppire_data_editor_refresh_model (de); - gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget, - TRUE, TRUE, 0); gtk_widget_show_all (de->vbox); if (de->font) @@ -871,14 +527,9 @@ psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split) void psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index) { - PsppireDataSheet *data_sheet; switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de))) { - case PSPPIRE_DATA_EDITOR_DATA_VIEW: - data_sheet = psppire_data_editor_get_active_data_sheet (de); - psppire_data_sheet_goto_variable (data_sheet, dict_index); - break; case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW: psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet), @@ -887,41 +538,6 @@ psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index) } } -/* Returns the "active" data sheet in DE. If DE is in single-paned mode, this - is the only data sheet. If DE is in split mode (showing four data sheets), - this is the focused data sheet or, if none is focused, the data sheet with - selected cells or, if none has selected cells, the upper-left data sheet. */ -PsppireDataSheet * -psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de) -{ - if (de->split) - { - PsppireDataSheet *data_sheet; - GtkWidget *scroller; - int i; - - /* If one of the datasheet's scrollers is focused, choose that one. */ - scroller = gtk_container_get_focus_child ( - GTK_CONTAINER (de->datasheet_vbox_widget)); - if (scroller != NULL) - return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller))); - - /* Otherwise if there's a nonempty selection in some data sheet, choose - that one. */ - FOR_EACH_DATA_SHEET (data_sheet, i, de) - { - PsppSheetSelection *selection; - - selection = pspp_sheet_view_get_selection ( - PSPP_SHEET_VIEW (data_sheet)); - if (pspp_sheet_selection_count_selected_rows (selection) - && pspp_sheet_selection_count_selected_columns (selection)) - return data_sheet; - } - } - - return PSPPIRE_DATA_SHEET (de->data_sheets[0]); -} /* Returns the UI manager that should be merged into DE's toplevel widget's UI manager to display menu items and toolbar items specific to DE's current @@ -939,7 +555,6 @@ psppire_data_editor_get_ui_manager (PsppireDataEditor *de) static void psppire_data_editor_update_ui_manager (PsppireDataEditor *de) { - PsppireDataSheet *data_sheet; GtkUIManager *ui_manager; ui_manager = NULL; @@ -947,13 +562,6 @@ psppire_data_editor_update_ui_manager (PsppireDataEditor *de) switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de))) { case PSPPIRE_DATA_EDITOR_DATA_VIEW: - data_sheet = psppire_data_editor_get_active_data_sheet (de); - if (data_sheet != NULL) - ui_manager = psppire_data_sheet_get_ui_manager (data_sheet); - else - { - /* This happens transiently in psppire_data_editor_split_window(). */ - } break; case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW: @@ -977,3 +585,5 @@ psppire_data_editor_update_ui_manager (PsppireDataEditor *de) g_object_notify (G_OBJECT (de), "ui-manager"); } } + + diff --git a/src/ui/gui/psppire-data-editor.h b/src/ui/gui/psppire-data-editor.h index 81495d5120..e75ccd7819 100644 --- a/src/ui/gui/psppire-data-editor.h +++ b/src/ui/gui/psppire-data-editor.h @@ -61,13 +61,13 @@ struct _PsppireDataEditor /* Variable sheet tab. */ GtkWidget *var_sheet; + GtkWidget *data_sheet; /* Data sheet tab. */ GtkWidget *vbox; /* Top-level widget in tab. */ GtkWidget *cell_ref_label; /* GtkLabel that shows selected case and var. */ GtkWidget *datum_entry; /* PsppireValueEntry for editing current cell. */ - GtkWidget *datasheet_vbox_widget; /* ->vbox child that holds data sheets. */ - GtkWidget *data_sheets[4]; /* Normally one data sheet; four, if split. */ + gboolean split; /* True if data sheets are split. */ /* UI manager for whichever var or data sheet is currently in use. */ diff --git a/src/ui/gui/psppire-data-sheet.c b/src/ui/gui/psppire-data-sheet.c index dbc9215977..cce71873b9 100644 --- a/src/ui/gui/psppire-data-sheet.c +++ b/src/ui/gui/psppire-data-sheet.c @@ -15,2521 +15,3 @@ along with this program. If not, see . */ #include - -#include "ui/gui/psppire-data-sheet.h" - -#include "data/case-map.h" -#include "data/casereader.h" -#include "data/casewriter.h" -#include "data/data-out.h" -#include "data/datasheet.h" -#include "data/format.h" -#include "data/value-labels.h" -#include "libpspp/intern.h" -#include "libpspp/range-set.h" -#include "ui/gui/executor.h" -#include "ui/gui/find-dialog.h" -#include "ui/gui/goto-case-dialog.h" -#include "ui/gui/builder-wrapper.h" -#include "ui/gui/helper.h" -#include "ui/gui/pspp-sheet-selection.h" -#include "ui/gui/psppire-cell-renderer-button.h" -#include "ui/gui/psppire-data-store.h" -#include "ui/gui/psppire-data-window.h" -#include "ui/gui/psppire-dialog-action-var-info.h" -#include "ui/gui/psppire-empty-list-store.h" -#include "ui/gui/psppire-marshal.h" - -#include "gl/intprops.h" -#include "gl/xalloc.h" - -#include -#define _(msgid) gettext (msgid) -#define N_(msgid) msgid - -static void psppire_data_sheet_dispose (GObject *); -static void psppire_data_sheet_unset_data_store (PsppireDataSheet *); - -static void psppire_data_sheet_update_clip_actions (PsppireDataSheet *); -static void psppire_data_sheet_update_primary_selection (PsppireDataSheet *, - gboolean should_own); -static void psppire_data_sheet_set_clip (PsppireDataSheet *, gboolean cut); - -static void on_selection_changed (PsppSheetSelection *, gpointer); -static void on_owner_change (GtkClipboard *, GdkEventOwnerChange *, gpointer); -static void psppire_data_sheet_clip_received_cb (GtkClipboard *, - GtkSelectionData *, gpointer); - -G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, PSPP_TYPE_SHEET_VIEW); - -static gboolean -get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip, - gint wx, gint wy, - size_t *row, PsppSheetViewColumn **columnp) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - gint bx, by; - GtkTreePath *path; - GtkTreeIter iter; - PsppSheetViewColumn *tree_column; - GtkTreeModel *tree_model; - bool ok; - - /* Check that WIDGET is really visible on the screen before we - do anything else. This is a bug fix for a sticky situation: - when text_data_import_assistant() returns, it frees the data - necessary to compose the tool tip message, but there may be - a tool tip under preparation at that point (even if there is - no visible tool tip) that will call back into us a little - bit later. Perhaps the correct solution to this problem is - 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_get_mapped (widget)) - return FALSE; - - pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, - wx, wy, &bx, &by); - if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by, - &path, &tree_column, NULL, NULL)) - return FALSE; - - *columnp = tree_column; - - pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column, - NULL); - - 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 = GPOINTER_TO_INT (iter.user_data); - return TRUE; -} - -static gboolean -on_query_tooltip (GtkWidget *widget, gint wx, gint wy, - gboolean keyboard_mode UNUSED, - GtkTooltip *tooltip, gpointer data UNUSED) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - PsppSheetViewColumn *column; - struct variable *var; - const char *label; - union value v; - size_t row; - int width; - - g_return_val_if_fail (data_store != NULL, FALSE); - g_return_val_if_fail (data_store->datasheet != NULL, FALSE); - - if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column)) - return FALSE; - - var = g_object_get_data (G_OBJECT (column), "variable"); - if (var == NULL) - { - if (g_object_get_data (G_OBJECT (column), "new-var-column") == NULL) - return FALSE; - - gtk_tooltip_set_text (tooltip, - _("Enter a number to add a new variable.")); - return TRUE; - } - else if (row >= datasheet_get_n_rows (data_store->datasheet)) - { - gtk_tooltip_set_text (tooltip, _("Enter a number to add a new case.")); - return TRUE; - } - - width = var_get_width (var); - - value_init (&v, width); - datasheet_get_value (data_store->datasheet, row, var_get_case_index (var), - &v); - - label = var_lookup_value_label (var, &v); - if (label != NULL) - { - if (data_sheet->show_value_labels) - { - char *s = value_to_text (v, var); - gtk_tooltip_set_text (tooltip, s); - free (s); - } - else - gtk_tooltip_set_text (tooltip, label); - } - value_destroy (&v, width); - - return label != NULL; -} - -static void -render_row_number_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer store_) -{ - PsppireDataStore *store = store_; - GValue gvalue = { 0, }; - gint row = GPOINTER_TO_INT (iter->user_data); - - g_return_if_fail (store->datasheet); - - g_value_init (&gvalue, G_TYPE_INT); - g_value_set_int (&gvalue, row + 1); - g_object_set_property (G_OBJECT (cell), "label", &gvalue); - g_value_unset (&gvalue); - - if (row < datasheet_get_n_rows (store->datasheet)) - g_object_set (cell, "editable", TRUE, NULL); - else - g_object_set (cell, "editable", FALSE, NULL); - - g_object_set (cell, - "slash", psppire_data_store_filtered (store, row), - NULL); -} - -static void -on_row_number_clicked (PsppireCellRendererButton *button, - gchar *path_string, - PsppSheetView *sheet_view) -{ - PsppSheetSelection *selection; - GtkTreePath *path; - - path = gtk_tree_path_new_from_string (path_string); - - selection = pspp_sheet_view_get_selection (sheet_view); - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_path (selection, path); - pspp_sheet_selection_select_all_columns (selection); - - gtk_tree_path_free (path); -} - -static void -make_row_number_column (PsppireDataSheet *data_sheet, - PsppireDataStore *ds) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetViewColumn *column; - GtkCellRenderer *renderer; - - renderer = psppire_cell_renderer_button_new (); - g_object_set (renderer, "xalign", 1.0, NULL); - g_signal_connect (renderer, "clicked", G_CALLBACK (on_row_number_clicked), - sheet_view); - - column = pspp_sheet_view_column_new_with_attributes (_("Case"), - renderer, NULL); - pspp_sheet_view_column_set_selectable (column, TRUE); - pspp_sheet_view_column_set_row_head (column, TRUE); - pspp_sheet_view_column_set_tabbable (column, FALSE); - pspp_sheet_view_column_set_clickable (column, TRUE); - pspp_sheet_view_column_set_cell_data_func ( - column, renderer, render_row_number_cell, ds, NULL); - pspp_sheet_view_column_set_fixed_width (column, 50); - pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers); - pspp_sheet_view_append_column (sheet_view, column); -} - -static void -render_data_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data_sheet_) -{ - PsppireDataSheet *data_sheet = data_sheet_; - PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet); - struct variable *var; - gchar *string; - gint row; - - double xalign; - - row = GPOINTER_TO_INT (iter->user_data); - var = g_object_get_data (G_OBJECT (tree_column), "variable"); - - string = psppire_data_store_get_string (store, row, var, - data_sheet->show_value_labels); - if (string != NULL) - { - GValue gvalue = { 0 }; - - g_value_init (&gvalue, G_TYPE_STRING); - g_value_take_string (&gvalue, string); - g_object_set_property (G_OBJECT (cell), "text", &gvalue); - g_value_unset (&gvalue); - } - else - g_object_set (G_OBJECT (cell), "text", "", NULL); - - switch (var_get_alignment (var)) - { - case ALIGN_LEFT: xalign = 0.0; break; - case ALIGN_RIGHT: xalign = 1.0; break; - case ALIGN_CENTRE: xalign = 0.5; break; - default: xalign = 0.0; break; - } - g_object_set (cell, - "xalign", xalign, - "editable", TRUE, - NULL); -} - -static gint -get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer, - const char *string) -{ - gint width; - g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL); - gtk_cell_renderer_get_preferred_width (renderer, GTK_WIDGET (treeview), - NULL, &width); - - return width; -} - -static gint -get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer, - size_t char_cnt) -{ - struct string s; - gint width; - - ds_init_empty (&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); - - return width; -} - -static void -on_data_column_editing_started (GtkCellRenderer *cell, - GtkCellEditable *editable, - const gchar *path, - gpointer user_data) -{ - PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column"); - PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet"); - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - struct variable *var; - - g_return_if_fail (column); - g_return_if_fail (data_sheet); - g_return_if_fail (data_store); - - - g_object_ref (editable); - g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable", - editable, g_object_unref); - - var = g_object_get_data (G_OBJECT (column), "variable"); - g_return_if_fail (var); - - if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable)) - { - const struct val_labs *labels = var_get_value_labels (var); - const struct val_lab **vls = val_labs_sorted (labels); - size_t n_vls = val_labs_count (labels); - GtkListStore *list_store; - int i; - - list_store = gtk_list_store_new (1, G_TYPE_STRING); - for (i = 0; i < n_vls; ++i) - { - const struct val_lab *vl = vls[i]; - GtkTreeIter iter; - - gtk_list_store_append (list_store, &iter); - gtk_list_store_set (list_store, &iter, - 0, val_lab_get_label (vl), - -1); - } - free (vls); - - gtk_combo_box_set_model (GTK_COMBO_BOX (editable), - GTK_TREE_MODEL (list_store)); - g_object_unref (list_store); - } -} - -static void -scroll_to_bottom (GtkWidget *widget, - GtkRequisition *requisition, - gpointer unused UNUSED) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget); - GtkAdjustment *vadjust; - - vadjust = pspp_sheet_view_get_vadjustment (sheet_view); - gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust)); - - if (data_sheet->scroll_to_bottom_signal) - { - g_signal_handler_disconnect (data_sheet, - data_sheet->scroll_to_bottom_signal); - data_sheet->scroll_to_bottom_signal = 0; - } -} - -static void -on_data_column_edited (GtkCellRendererText *cell, - gchar *path_string, - gchar *new_text, - gpointer user_data) -{ - PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column"); - PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet"); - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - GtkEditable *editable; - struct variable *var; - GtkTreePath *path; - gboolean is_val_lab; - gboolean new_row; - gint row; - - path = gtk_tree_path_new_from_string (path_string); - row = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - - var = g_object_get_data (G_OBJECT (column), "variable"); - - new_row = row == psppire_data_store_get_case_count (data_store); - if (new_row && new_text[0] == '\0') - return; - - editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable"); - g_return_if_fail (editable != NULL); - is_val_lab = (GTK_IS_COMBO_BOX (editable) - && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0); - g_object_unref (editable); - - psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab); - - if (new_row && !data_sheet->scroll_to_bottom_signal) - { - gtk_widget_queue_resize (GTK_WIDGET (data_sheet)); - data_sheet->scroll_to_bottom_signal = - g_signal_connect (data_sheet, "size-allocate", - G_CALLBACK (scroll_to_bottom), NULL); - } - else - { - /* We could be more specific about what to redraw, if it seems - important for performance. */ - gtk_widget_queue_draw (GTK_WIDGET (data_sheet)); - } -} - -static void -scroll_to_right (GtkWidget *widget, - PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetViewColumn *column, *prev; - GList *columns, *iter; - - column = NULL; - prev = NULL; - columns = pspp_sheet_view_get_columns (sheet_view); - for (iter = columns; iter; iter = iter->next) - { - PsppSheetViewColumn *c = iter->data; - if (g_object_get_data (G_OBJECT (c), "new-var-column")) - { - column = c; - break; - } - prev = c; - } - g_list_free (columns); - - if (column == NULL) - return; - - pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0); - - if (prev) - { - GtkTreePath *path; - - pspp_sheet_view_get_cursor (sheet_view, &path, NULL); - if (path) - { - pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE); - gtk_tree_path_free (path); - } - } - - if (data_sheet->scroll_to_right_signal) - { - g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal); - data_sheet->scroll_to_right_signal = 0; - } -} - -static void -on_new_variable_column_edited (GtkCellRendererText *cell, - gchar *path_string, - gchar *new_text, - gpointer user_data) -{ - PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet"); - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - PsppireDict *dict = data_store->dict; - struct variable *var; - GtkTreePath *path; - char name[64]; - gint row; - - if (new_text[0] == '\0') - { - /* User didn't enter anything so don't create a variable. */ - return; - } - - path = gtk_tree_path_new_from_string (path_string); - row = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - - if (!psppire_dict_generate_name (dict, name, sizeof name)) - return; - - var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict), - name); - g_return_if_fail (var != NULL); - - psppire_data_store_set_string (data_store, new_text, row, var, FALSE); - - if (!data_sheet->scroll_to_right_signal) - { - gtk_widget_queue_resize (GTK_WIDGET (data_sheet)); - data_sheet->scroll_to_right_signal = - g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize", - G_CALLBACK (scroll_to_right), data_sheet); - } - else - { - /* We could be more specific about what to redraw, if it seems - important for performance. */ - gtk_widget_queue_draw (GTK_WIDGET (data_sheet)); - } -} - -static void -calc_width_conversion (PsppireDataSheet *data_sheet, - gint *base_width, gint *incr_width) -{ - GtkCellRenderer *cell; - gint w1, w10; - - cell = gtk_cell_renderer_text_new (); - w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1); - w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10); - *incr_width = MAX (1, (w10 - w1) / 9); - *base_width = MAX (0, w10 - *incr_width * 10); - g_object_ref_sink (cell); - g_object_unref (cell); -} - -static gint -display_width_from_pixel_width (PsppireDataSheet *data_sheet, - gint pixel_width) -{ - gint base_width, incr_width; - - calc_width_conversion (data_sheet, &base_width, &incr_width); - return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1); -} - -static gint -display_width_to_pixel_width (PsppireDataSheet *data_sheet, - gint display_width, - gint base_width, - gint incr_width) -{ - return base_width + incr_width * display_width; -} - -static void -on_data_column_resized (GObject *gobject, - GParamSpec *pspec, - gpointer user_data) -{ - PsppireDataSheet *data_sheet = user_data; - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject); - struct variable *var; - gint pixel_width; - int display_width; - - if (data_store == NULL) - return; - - pixel_width = pspp_sheet_view_column_get_width (column); - if (pixel_width == pspp_sheet_view_column_get_fixed_width (column)) - { - /* Short-circuit the expensive display_width_from_pixel_width() - calculation, to make loading .sav files with 2000 columns visibly - faster. */ - return; - } - - var = g_object_get_data (G_OBJECT (column), "variable"); - display_width = display_width_from_pixel_width (data_sheet, pixel_width); - var_set_display_width (var, display_width); -} - -static void -do_data_column_popup_menu (PsppSheetViewColumn *column, - guint button, guint32 time) -{ - GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column); - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view); - GtkWidget *menu; - - menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup"); - gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time); -} - -static void -on_data_column_popup_menu (PsppSheetViewColumn *column, - gpointer user_data UNUSED) -{ - do_data_column_popup_menu (column, 0, gtk_get_current_event_time ()); -} - -static gboolean -on_column_button_press_event (PsppSheetViewColumn *column, - GdkEventButton *event, - gpointer user_data UNUSED) -{ - PsppSheetSelection *selection; - PsppSheetView *sheet_view; - - sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view ( - column)); - g_return_val_if_fail (sheet_view != NULL, FALSE); - - selection = pspp_sheet_view_get_selection (sheet_view); - g_return_val_if_fail (selection != NULL, FALSE); - - if (event->type == GDK_BUTTON_PRESS && event->button == 3) - { - do_data_column_popup_menu (column, event->button, event->time); - return TRUE; - } - else if (event->type == GDK_2BUTTON_PRESS && event->button == 1) - { - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view); - struct variable *var; - - var = g_object_get_data (G_OBJECT (column), "variable"); - if (var != NULL) - { - gboolean handled; - - g_signal_emit_by_name (data_sheet, "var-double-clicked", - var_get_dict_index (var), &handled); - return handled; - } - } - - return FALSE; -} - -static gboolean -on_data_column_query_tooltip (PsppSheetViewColumn *column, - GtkTooltip *tooltip, - gpointer user_data UNUSED) -{ - struct variable *var; - const char *text; - - var = g_object_get_data (G_OBJECT (column), "variable"); - g_return_val_if_fail (var != NULL, FALSE); - - text = var_has_label (var) ? var_get_label (var) : var_get_name (var); - gtk_tooltip_set_text (tooltip, text); - - return TRUE; -} - -static void -add_data_column_cell_renderer (PsppireDataSheet *data_sheet, - PsppSheetViewColumn *column) -{ - GtkCellRenderer *cell; - struct variable *var; - - var = g_object_get_data (G_OBJECT (column), "variable"); - g_return_if_fail (var != NULL); - - if (data_sheet->show_value_labels && var_has_value_labels (var)) - { - cell = gtk_cell_renderer_combo_new (); - g_object_set (G_OBJECT (cell), - "has-entry", TRUE, - "text-column", 0, - NULL); - } - else - cell = gtk_cell_renderer_text_new (); - - g_signal_connect (cell, "editing-started", - G_CALLBACK (on_data_column_editing_started), NULL); - g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL); - - g_object_set_data (G_OBJECT (cell), "column", column); - g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet); - - pspp_sheet_view_column_clear (column); - pspp_sheet_view_column_pack_start (column, cell, TRUE); - - pspp_sheet_view_column_set_cell_data_func ( - column, cell, render_data_cell, data_sheet, NULL); -} - -static PsppSheetViewColumn * -make_data_column (PsppireDataSheet *data_sheet, gint dict_idx, - gint base_width, gint incr_width) -{ - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - struct variable *var; - PsppSheetViewColumn *column; - char *name; - int width; - - var = psppire_dict_get_variable (data_store->dict, dict_idx); - - column = pspp_sheet_view_column_new (); - - name = escape_underscores (var_get_name (var)); - pspp_sheet_view_column_set_title (column, name); - free (name); - - g_object_set_data (G_OBJECT (column), "variable", var); - - width = display_width_to_pixel_width (data_sheet, - var_get_display_width (var), - base_width, incr_width); - pspp_sheet_view_column_set_min_width (column, 10); - pspp_sheet_view_column_set_fixed_width (column, width); - pspp_sheet_view_column_set_resizable (column, TRUE); - - pspp_sheet_view_column_set_clickable (column, TRUE); - g_signal_connect (column, "notify::width", - G_CALLBACK (on_data_column_resized), data_sheet); - - g_signal_connect (column, "button-press-event", - G_CALLBACK (on_column_button_press_event), - data_sheet); - g_signal_connect (column, "query-tooltip", - G_CALLBACK (on_data_column_query_tooltip), NULL); - g_signal_connect (column, "popup-menu", - G_CALLBACK (on_data_column_popup_menu), data_sheet); - - add_data_column_cell_renderer (data_sheet, column); - - return column; -} - -static void -make_new_variable_column (PsppireDataSheet *data_sheet, - gint base_width, gint incr_width) -{ - PsppSheetViewColumn *column; - GtkCellRenderer *cell; - int width; - - cell = gtk_cell_renderer_text_new (); - g_object_set (cell, "editable", TRUE, NULL); - - g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited), - NULL); - - column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL); - g_object_set_data (G_OBJECT (column), "new-var-column", column); - - width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width); - pspp_sheet_view_column_set_min_width (column, 10); - pspp_sheet_view_column_set_fixed_width (column, width); - pspp_sheet_view_column_set_tabbable (column, FALSE); - - g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet); - g_signal_connect (column, "button-press-event", - G_CALLBACK (on_column_button_press_event), - data_sheet); - g_signal_connect (column, "popup-menu", - G_CALLBACK (on_data_column_popup_menu), data_sheet); - - pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars); - - pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column); - data_sheet->new_variable_column = column; -} - -static void -psppire_data_sheet_model_changed (GObject *gobject, - GParamSpec *pspec, - gpointer user_data) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject); - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *data_store; - - /* Remove old columns. */ - for (;;) - { - PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0); - if (column == NULL) - break; - - pspp_sheet_view_remove_column (sheet_view, column); - } - data_sheet->new_variable_column = NULL; - - if (pspp_sheet_view_get_model (sheet_view) == NULL) - { - /* Don't create any columns at all if there's no model. Otherwise we'll - create some columns as part of the "dispose" callback for the sheet - view, which sets the model to NULL. That causes warnings to be - logged and is obviously undesirable in any case. */ - return; - } - - /* Add new columns. */ - data_store = psppire_data_sheet_get_data_store (data_sheet); - if (data_store != NULL) - { - gint base_width, incr_width; - int i; - - calc_width_conversion (data_sheet, &base_width, &incr_width); - - make_row_number_column (data_sheet, data_store); - for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++) - { - PsppSheetViewColumn *column; - - column = make_data_column (data_sheet, i, base_width, incr_width); - pspp_sheet_view_append_column (sheet_view, column); - } - make_new_variable_column (data_sheet, base_width, incr_width); - } -} - -enum - { - PROP_0, - PROP_DATA_STORE, - PROP_VALUE_LABELS, - PROP_CASE_NUMBERS, - PROP_CURRENT_CASE, - PROP_MAY_CREATE_VARS, - PROP_MAY_DELETE_VARS, - PROP_UI_MANAGER - }; - -static void -psppire_data_sheet_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object); - - switch (prop_id) - { - case PROP_DATA_STORE: - psppire_data_sheet_set_data_store ( - obj, PSPPIRE_DATA_STORE (g_value_get_object (value))); - break; - - case PROP_VALUE_LABELS: - psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value)); - break; - - case PROP_CASE_NUMBERS: - psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value)); - break; - - case PROP_CURRENT_CASE: - psppire_data_sheet_goto_case (obj, g_value_get_long (value)); - break; - - case PROP_MAY_CREATE_VARS: - psppire_data_sheet_set_may_create_vars (obj, - g_value_get_boolean (value)); - break; - - case PROP_MAY_DELETE_VARS: - psppire_data_sheet_set_may_delete_vars (obj, - g_value_get_boolean (value)); - break; - - case PROP_UI_MANAGER: - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_data_sheet_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object); - - switch (prop_id) - { - case PROP_DATA_STORE: - g_value_set_object (value, psppire_data_sheet_get_data_store (obj)); - break; - - case PROP_VALUE_LABELS: - g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj)); - break; - - case PROP_CASE_NUMBERS: - g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj)); - break; - - case PROP_CURRENT_CASE: - g_value_set_long (value, psppire_data_sheet_get_selected_case (obj)); - break; - - case PROP_MAY_CREATE_VARS: - g_value_set_boolean (value, obj->may_create_vars); - break; - - case PROP_MAY_DELETE_VARS: - g_value_set_boolean (value, obj->may_delete_vars); - break; - - case PROP_UI_MANAGER: - g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -gboolean -psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds) -{ - return ds->show_value_labels; -} - -void -psppire_data_sheet_set_value_labels (PsppireDataSheet *ds, - gboolean show_value_labels) -{ - show_value_labels = !!show_value_labels; - if (show_value_labels != ds->show_value_labels) - { - ds->show_value_labels = show_value_labels; - g_object_notify (G_OBJECT (ds), "value-labels"); - - /* Pretend the model changed, to force the columns to be rebuilt. - Otherwise cell renderers won't get changed from combo boxes to text - entries or vice versa. */ - g_object_notify (G_OBJECT (ds), "model"); - } -} - -gboolean -psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds) -{ - return ds->show_case_numbers; -} - -void -psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds, - gboolean show_case_numbers) -{ - show_case_numbers = !!show_case_numbers; - if (show_case_numbers != ds->show_case_numbers) - { - PsppSheetViewColumn *column; - - ds->show_case_numbers = show_case_numbers; - column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0); - if (column) - pspp_sheet_view_column_set_visible (column, show_case_numbers); - - g_object_notify (G_OBJECT (ds), "case-numbers"); - gtk_widget_queue_draw (GTK_WIDGET (ds)); - } -} - -gboolean -psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet) -{ - return data_sheet->may_create_vars; -} - -void -psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet, - gboolean may_create_vars) -{ - if (data_sheet->may_create_vars != may_create_vars) - { - data_sheet->may_create_vars = may_create_vars; - if (data_sheet->new_variable_column) - pspp_sheet_view_column_set_visible (data_sheet->new_variable_column, - may_create_vars); - - on_selection_changed (pspp_sheet_view_get_selection ( - PSPP_SHEET_VIEW (data_sheet)), NULL); - } -} - -gboolean -psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet) -{ - return data_sheet->may_delete_vars; -} - -void -psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet, - gboolean may_delete_vars) -{ - if (data_sheet->may_delete_vars != may_delete_vars) - { - data_sheet->may_delete_vars = may_delete_vars; - on_selection_changed (pspp_sheet_view_get_selection ( - PSPP_SHEET_VIEW (data_sheet)), NULL); - } -} - -static PsppSheetViewColumn * -psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet, - gint dict_index) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *data_store; - PsppSheetViewColumn *column; - struct variable *var; - GList *list, *iter; - - data_store = psppire_data_sheet_get_data_store (data_sheet); - g_return_val_if_fail (data_store != NULL, NULL); - g_return_val_if_fail (data_store->dict != NULL, NULL); - - var = psppire_dict_get_variable (data_store->dict, dict_index); - g_return_val_if_fail (var != NULL, NULL); - - column = NULL; - list = pspp_sheet_view_get_columns (sheet_view); - for (iter = list; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *c = iter->data; - struct variable *v; - - v = g_object_get_data (G_OBJECT (c), "variable"); - if (v == var) - { - column = c; - break; - } - } - g_list_free (list); - - return column; -} - -void -psppire_data_sheet_goto_variable (PsppireDataSheet *data_sheet, - gint dict_index) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetViewColumn *column; - - column = psppire_data_sheet_find_column_for_variable (data_sheet, - dict_index); - if (column != NULL) - { - GtkTreePath *path; - - gint row = psppire_data_sheet_get_current_case (data_sheet); - path = gtk_tree_path_new_from_indices (row >= 0 ? row : 0, -1); - - pspp_sheet_view_scroll_to_cell (sheet_view, path, column, - FALSE, 0.0, 0.0); - pspp_sheet_view_set_cursor (sheet_view, path, column, FALSE); - gtk_tree_path_free (path); - } -} - -struct variable * -psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet) -{ - PsppSheetSelection *selection; - struct variable *var; - GList *selected_columns; - GList *iter; - - selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet)); - selected_columns = pspp_sheet_selection_get_selected_columns (selection); - - var = NULL; - for (iter = selected_columns; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *v = g_object_get_data (G_OBJECT (column), "variable"); - if (v != NULL) - { - if (var) - { - var = NULL; - break; - } - else - var = v; - } - } - - g_list_free (selected_columns); - - return var; - -} -void -psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *store = data_sheet->data_store; - PsppSheetSelection *selection; - GtkTreePath *path; - - g_return_if_fail (case_index >= 0); - g_return_if_fail (case_index < psppire_data_store_get_case_count (store)); - - path = gtk_tree_path_new_from_indices (case_index, -1); - - /* Select the case. */ - selection = pspp_sheet_view_get_selection (sheet_view); - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_path (selection, path); - pspp_sheet_selection_select_all_columns (selection); - - /* Scroll so that the case is visible. */ - pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0); - - gtk_tree_path_free (path); -} - -/* Returns the 0-based index of a selected case, if there is at least one, and - -1 otherwise. - - If more than one case is selected, returns the one with the smallest index, - that is, the index of the case closest to the beginning of the file. The - row that can be used to insert a new case is not considered a case. */ -gint -psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *store = data_sheet->data_store; - const struct range_set_node *node; - PsppSheetSelection *selection; - struct range_set *rows; - gint row; - - selection = pspp_sheet_view_get_selection (sheet_view); - rows = pspp_sheet_selection_get_range_set (selection); - node = range_set_first (rows); - row = (node && node->start < psppire_data_store_get_case_count (store) - ? node->start - : -1); - range_set_destroy (rows); - - return row; -} - -/* Returns the 0-based index of a selected case, if exactly one case is - selected, and -1 otherwise. Returns -1 if the row that can be used to - insert a new case is selected. */ -gint -psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *store = data_sheet->data_store; - const struct range_set_node *node; - PsppSheetSelection *selection; - struct range_set *rows; - gint row; - - selection = pspp_sheet_view_get_selection (sheet_view); - if (pspp_sheet_selection_count_selected_rows (selection) != 1) - return -1; - - rows = pspp_sheet_selection_get_range_set (selection); - node = range_set_first (rows); - row = (node && node->start < psppire_data_store_get_case_count (store) - ? node->start - : -1); - range_set_destroy (rows); - - return row; -} - -GtkUIManager * -psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet) -{ - if (data_sheet->uim == NULL) - { - data_sheet->uim = - GTK_UI_MANAGER (get_object_assert (data_sheet->builder, - "data_sheet_uim", - GTK_TYPE_UI_MANAGER)); - g_object_ref (data_sheet->uim); - } - - return data_sheet->uim; -} - -static void -psppire_data_sheet_dispose (GObject *object) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object); - - if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0) - { - g_signal_handler_disconnect (data_sheet->clip, - data_sheet->on_owner_change_signal); - data_sheet->on_owner_change_signal = 0; - } - - if (data_sheet->dispose_has_run) - return; - - data_sheet->dispose_has_run = TRUE; - - psppire_data_sheet_unset_data_store (data_sheet); - - g_object_unref (data_sheet->builder); - - if (data_sheet->uim) - g_object_unref (data_sheet->uim); - - G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object); -} - -static void -psppire_data_sheet_map (GtkWidget *widget) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); - - GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget); - - data_sheet->clip = gtk_widget_get_clipboard (widget, - GDK_SELECTION_CLIPBOARD); - if (data_sheet->on_owner_change_signal) - g_signal_handler_disconnect (data_sheet->clip, - data_sheet->on_owner_change_signal); - data_sheet->on_owner_change_signal - = g_signal_connect (data_sheet->clip, "owner-change", - G_CALLBACK (on_owner_change), widget); - on_owner_change (data_sheet->clip, NULL, widget); -} - -static void -psppire_data_sheet_class_init (PsppireDataSheetClass *class) -{ - GObjectClass *gobject_class; - GtkWidgetClass *widget_class; - - gobject_class = G_OBJECT_CLASS (class); - gobject_class->set_property = psppire_data_sheet_set_property; - gobject_class->get_property = psppire_data_sheet_get_property; - gobject_class->dispose = psppire_data_sheet_dispose; - - widget_class = GTK_WIDGET_CLASS (class); - widget_class->map = psppire_data_sheet_map; - - g_signal_new ("var-double-clicked", - G_OBJECT_CLASS_TYPE (gobject_class), - G_SIGNAL_RUN_LAST, - 0, - g_signal_accumulator_true_handled, NULL, - psppire_marshal_BOOLEAN__INT, - G_TYPE_BOOLEAN, 1, G_TYPE_INT); - - g_object_class_install_property ( - gobject_class, PROP_DATA_STORE, - g_param_spec_object ("data-store", - "Data Store", - "The data store for the data sheet to display.", - PSPPIRE_TYPE_DATA_STORE, - G_PARAM_WRITABLE | G_PARAM_READABLE)); - - g_object_class_install_property ( - gobject_class, PROP_VALUE_LABELS, - g_param_spec_boolean ("value-labels", - "Value Labels", - "Whether or not the data sheet should display labels instead of values", - FALSE, - G_PARAM_WRITABLE | G_PARAM_READABLE)); - - g_object_class_install_property ( - gobject_class, PROP_CASE_NUMBERS, - g_param_spec_boolean ("case-numbers", - "Case Numbers", - "Whether or not the data sheet should display case numbers", - FALSE, - G_PARAM_WRITABLE | G_PARAM_READABLE)); - - g_object_class_install_property ( - gobject_class, - PROP_CURRENT_CASE, - g_param_spec_long ("current-case", - "Current Case", - "Zero based number of the selected case", - 0, CASENUMBER_MAX, - 0, - G_PARAM_WRITABLE | G_PARAM_READABLE)); - - g_object_class_install_property ( - gobject_class, - PROP_MAY_CREATE_VARS, - g_param_spec_boolean ("may-create-vars", - "May create variables", - "Whether the user may create more variables", - TRUE, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - gobject_class, - PROP_MAY_DELETE_VARS, - g_param_spec_boolean ("may-delete-vars", - "May delete variables", - "Whether the user may delete variables", - TRUE, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - gobject_class, - PROP_UI_MANAGER, - g_param_spec_object ("ui-manager", - "UI Manager", - "UI manager for the data sheet. The client should merge this UI manager with the active UI manager to obtain data sheet specific menu items and tool bar items.", - GTK_TYPE_UI_MANAGER, - G_PARAM_READABLE)); -} - -static void -do_popup_menu (GtkWidget *widget, guint button, guint32 time) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); - GtkWidget *menu; - - menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup"); - gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time); -} - -static void -on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED) -{ - do_popup_menu (widget, 0, gtk_get_current_event_time ()); -} - -static gboolean -on_button_pressed (GtkWidget *widget, GdkEventButton *event, - gpointer user_data UNUSED) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget); - - if (event->type == GDK_BUTTON_PRESS && event->button == 3) - { - PsppSheetSelection *selection; - - selection = pspp_sheet_view_get_selection (sheet_view); - if (pspp_sheet_selection_count_selected_rows (selection) <= 1) - { - GtkTreePath *path; - - if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y, - &path, NULL, NULL, NULL)) - { - pspp_sheet_selection_unselect_all (selection); - pspp_sheet_selection_select_path (selection, path); - pspp_sheet_selection_select_all_columns (selection); - gtk_tree_path_free (path); - } - } - - do_popup_menu (widget, event->button, event->time); - - return TRUE; - } - - return FALSE; -} - -static void -on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - const struct range_set_node *node; - struct range_set *selected; - - selected = pspp_sheet_selection_get_range_set (selection); - for (node = range_set_last (selected); node != NULL; - node = range_set_prev (selected, node)) - { - unsigned long int start = range_set_node_get_start (node); - unsigned long int count = range_set_node_get_width (node); - - psppire_data_store_delete_cases (data_sheet->data_store, start, count); - } - range_set_destroy (selected); -} - -static void -on_selection_changed (PsppSheetSelection *selection, - gpointer user_data UNUSED) -{ - PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection); - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view); - gint n_selected_rows; - gboolean any_variables_selected; - gboolean may_delete_cases, may_delete_vars, may_insert_vars; - GList *list, *iter; - GtkTreePath *path; - GtkAction *action; - - n_selected_rows = pspp_sheet_selection_count_selected_rows (selection); - - action = get_action_assert (data_sheet->builder, "edit_insert-case"); - gtk_action_set_sensitive (action, n_selected_rows > 0); - - switch (n_selected_rows) - { - case 0: - may_delete_cases = FALSE; - break; - - case 1: - /* The row used for inserting new cases cannot be deleted. */ - path = gtk_tree_path_new_from_indices ( - psppire_data_store_get_case_count (data_sheet->data_store), -1); - may_delete_cases = !pspp_sheet_selection_path_is_selected (selection, - path); - gtk_tree_path_free (path); - break; - - default: - may_delete_cases = TRUE; - break; - } - action = get_action_assert (data_sheet->builder, "edit_clear-cases"); - gtk_action_set_sensitive (action, may_delete_cases); - - any_variables_selected = FALSE; - may_delete_vars = may_insert_vars = FALSE; - list = pspp_sheet_selection_get_selected_columns (selection); - for (iter = list; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *var = g_object_get_data (G_OBJECT (column), "variable"); - - if (var != NULL) - { - may_delete_vars = may_insert_vars = TRUE; - any_variables_selected = TRUE; - break; - } - if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL) - may_insert_vars = TRUE; - } - g_list_free (list); - - may_insert_vars = may_insert_vars && data_sheet->may_create_vars; - may_delete_vars = may_delete_vars && data_sheet->may_delete_vars; - - action = get_action_assert (data_sheet->builder, "edit_insert-variable"); - gtk_action_set_sensitive (action, may_insert_vars); - - action = get_action_assert (data_sheet->builder, "edit_clear-variables"); - gtk_action_set_sensitive (action, may_delete_vars); - - action = get_action_assert (data_sheet->builder, "sort-up"); - gtk_action_set_sensitive (action, may_delete_vars); - - action = get_action_assert (data_sheet->builder, "sort-down"); - gtk_action_set_sensitive (action, may_delete_vars); - - psppire_data_sheet_update_clip_actions (data_sheet); - psppire_data_sheet_update_primary_selection (data_sheet, - (n_selected_rows > 0 - && any_variables_selected)); -} - -static gboolean -psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet, - struct range_set **rowsp, - struct range_set **colsp) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppireDataStore *data_store = data_sheet->data_store; - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - unsigned long n_cases; - struct range_set *rows, *cols; - GList *list, *iter; - - if (data_store == NULL) - return FALSE; - n_cases = psppire_data_store_get_case_count (data_store); - - rows = pspp_sheet_selection_get_range_set (selection); - range_set_set0 (rows, n_cases, ULONG_MAX - n_cases); - if (range_set_is_empty (rows)) - { - range_set_destroy (rows); - return FALSE; - } - - cols = range_set_create (); - list = pspp_sheet_selection_get_selected_columns (selection); - for (iter = list; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *var = g_object_get_data (G_OBJECT (column), "variable"); - - if (var != NULL) - range_set_set1 (cols, var_get_dict_index (var), 1); - } - g_list_free (list); - if (range_set_is_empty (cols)) - { - range_set_destroy (rows); - range_set_destroy (cols); - return FALSE; - } - - *rowsp = rows; - *colsp = cols; - return TRUE; -} - -static void -on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDataStore *data_store = data_sheet->data_store; - struct range_set *selected; - unsigned long row; - - selected = pspp_sheet_selection_get_range_set (selection); - row = range_set_scan (selected, 0); - range_set_destroy (selected); - - if (row <= psppire_data_store_get_case_count (data_store)) - psppire_data_store_insert_new_case (data_store, row); -} - -static void -on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDict *dict = data_sheet->data_store->dict; - PsppSheetViewColumn *column; - struct variable *var; - gchar name[64]; - GList *list; - gint index; - - list = pspp_sheet_selection_get_selected_columns (selection); - if (list == NULL) - return; - column = list->data; - g_list_free (list); - - var = g_object_get_data (G_OBJECT (column), "variable"); - index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict); - if (psppire_dict_generate_name (dict, name, sizeof name)) - psppire_dict_insert_variable (dict, index, name); -} - -static void -on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDict *dict = data_sheet->data_store->dict; - GList *list, *iter; - - list = pspp_sheet_selection_get_selected_columns (selection); - if (list == NULL) - return; - list = g_list_reverse (list); - for (iter = list; iter; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *var; - - var = g_object_get_data (G_OBJECT (column), "variable"); - if (var != NULL) - psppire_dict_delete_variables (dict, var_get_dict_index (var), 1); - } - g_list_free (list); -} - -enum sort_order - { - SORT_ASCEND, - SORT_DESCEND - }; - -static void -do_sort (PsppireDataSheet *data_sheet, enum sort_order order) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDataWindow *pdw; - GList *list, *iter; - GString *syntax; - int n_vars; - - pdw = psppire_data_window_for_data_store (data_sheet->data_store); - g_return_if_fail (pdw != NULL); - - list = pspp_sheet_selection_get_selected_columns (selection); - - syntax = g_string_new ("SORT CASES BY"); - n_vars = 0; - for (iter = list; iter; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *var; - - var = g_object_get_data (G_OBJECT (column), "variable"); - if (var != NULL) - { - g_string_append_printf (syntax, " %s", var_get_name (var)); - n_vars++; - } - } - if (n_vars > 0) - { - if (order == SORT_DESCEND) - g_string_append (syntax, " (DOWN)"); - g_string_append_c (syntax, '.'); - execute_const_syntax_string (pdw, syntax->str); - } - g_string_free (syntax, TRUE); -} - -void -on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet) -{ - do_sort (data_sheet, SORT_ASCEND); -} - -void -on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet) -{ - do_sort (data_sheet, SORT_DESCEND); -} - -void -on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet) -{ - goto_case_dialog (data_sheet); -} - -void -on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet) -{ - PsppireDataWindow *pdw; - - pdw = psppire_data_window_for_data_store (data_sheet->data_store); - g_return_if_fail (pdw != NULL); - - find_dialog (pdw); -} - -void -on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet) -{ - psppire_data_sheet_set_clip (data_sheet, FALSE); -} - -void -on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet) -{ - psppire_data_sheet_set_clip (data_sheet, TRUE); -} - -void -on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet) -{ - GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet)); - GtkClipboard *clipboard = - gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD); - - gtk_clipboard_request_contents (clipboard, - gdk_atom_intern ("UTF8_STRING", TRUE), - psppire_data_sheet_clip_received_cb, - data_sheet); -} - -static void -psppire_data_sheet_init (PsppireDataSheet *obj) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj); - GtkAction *action; - - obj->show_value_labels = FALSE; - obj->show_case_numbers = TRUE; - obj->may_create_vars = TRUE; - obj->may_delete_vars = TRUE; - - obj->owns_primary_selection = FALSE; - - obj->scroll_to_bottom_signal = 0; - obj->scroll_to_right_signal = 0; - obj->on_owner_change_signal = 0; - obj->new_variable_column = NULL; - obj->container = NULL; - - obj->uim = NULL; - obj->dispose_has_run = FALSE; - - pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES); - - g_signal_connect (obj, "notify::model", - G_CALLBACK (psppire_data_sheet_model_changed), NULL); - - pspp_sheet_view_set_rubber_banding (sheet_view, TRUE); - pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view), - PSPP_SHEET_SELECTION_RECTANGLE); - - g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL); - g_signal_connect (obj, "query-tooltip", - G_CALLBACK (on_query_tooltip), NULL); - g_signal_connect (obj, "button-press-event", - G_CALLBACK (on_button_pressed), NULL); - g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL); - - obj->builder = builder_new ("data-sheet.ui"); - - action = get_action_assert (obj->builder, "edit_clear-cases"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases), - obj); - gtk_action_set_sensitive (action, FALSE); - g_signal_connect (pspp_sheet_view_get_selection (sheet_view), - "changed", G_CALLBACK (on_selection_changed), NULL); - - action = get_action_assert (obj->builder, "edit_insert-case"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case), - obj); - - action = get_action_assert (obj->builder, "edit_insert-variable"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable), - obj); - - action = get_action_assert (obj->builder, "edit_goto-case"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case), - obj); - - action = get_action_assert (obj->builder, "edit_copy"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj); - - action = get_action_assert (obj->builder, "edit_cut"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj); - - action = get_action_assert (obj->builder, "edit_paste"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj); - - action = get_action_assert (obj->builder, "edit_clear-variables"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables), - obj); - - action = get_action_assert (obj->builder, "edit_find"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj); - - action = get_action_assert (obj->builder, "sort-up"); - g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj); - - action = get_action_assert (obj->builder, "sort-down"); - g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj); - -} - -GtkWidget * -psppire_data_sheet_new (void) -{ - return g_object_new (PSPP_TYPE_DATA_SHEET, NULL); -} - -PsppireDataStore * -psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet) -{ - return data_sheet->data_store; -} - -static void -refresh_model (PsppireDataSheet *data_sheet) -{ - pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL); - - if (data_sheet->data_store != NULL) - { - PsppireEmptyListStore *model; - GtkAction *action; - int n_rows; - - n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1; - model = psppire_empty_list_store_new (n_rows); - pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), - GTK_TREE_MODEL (model)); - g_object_unref (model); - - action = get_action_assert (data_sheet->builder, "edit_copy"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), - data_sheet); - } -} - -static void -on_case_inserted (PsppireDataStore *data_store, gint row, - PsppireDataSheet *data_sheet) -{ - PsppireEmptyListStore *empty_list_store; - GtkTreeModel *tree_model; - gint n_rows; - - g_return_if_fail (data_store == data_sheet->data_store); - - n_rows = psppire_data_store_get_case_count (data_store) + 1; - if (row == n_rows - 1) - row++; - - tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet)); - empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model); - psppire_empty_list_store_set_n_rows (empty_list_store, n_rows); - psppire_empty_list_store_row_inserted (empty_list_store, row); -} - -static void -on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases, - PsppireDataSheet *data_sheet) -{ - - g_return_if_fail (data_store == data_sheet->data_store); - - if (n_cases > 1) - { - /* This is a bit of a cop-out. We could do better, if it ever turns out - that this performs too poorly. */ - refresh_model (data_sheet); - } - else - { - PsppireEmptyListStore *empty_list_store; - GtkTreeModel *tree_model; - gint n_rows = psppire_data_store_get_case_count (data_store) + 1; - - tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet)); - empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model); - psppire_empty_list_store_set_n_rows (empty_list_store, n_rows); - psppire_empty_list_store_row_deleted (empty_list_store, first); - } -} - -static void -on_case_change (PsppireDataStore *data_store, gint row, - PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - - pspp_sheet_view_stop_editing (sheet_view, TRUE); - gtk_widget_queue_draw (GTK_WIDGET (data_sheet)); -} - -static void -on_backend_changed (PsppireDataStore *data_store, - PsppireDataSheet *data_sheet) -{ - g_return_if_fail (data_store == data_sheet->data_store); - refresh_model (data_sheet); -} - -static void -on_variable_display_width_changed (PsppireDict *dict, int dict_index, - PsppireDataSheet *data_sheet) -{ - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - PsppSheetViewColumn *column; - struct variable *var; - int display_width; - gint pixel_width; - - g_return_if_fail (data_sheet->data_store != NULL); - g_return_if_fail (dict == data_sheet->data_store->dict); - - column = psppire_data_sheet_find_column_for_variable (data_sheet, - dict_index); - if (column == NULL) - return; - - var = psppire_dict_get_variable (data_store->dict, dict_index); - g_return_if_fail (var != NULL); - - pixel_width = pspp_sheet_view_column_get_fixed_width (column); - display_width = display_width_from_pixel_width (data_sheet, pixel_width); - if (display_width != var_get_display_width (var)) - { - gint base_width, incr_width; - - display_width = var_get_display_width (var); - calc_width_conversion (data_sheet, &base_width, &incr_width); - pixel_width = display_width_to_pixel_width (data_sheet, display_width, - base_width, incr_width); - pspp_sheet_view_column_set_fixed_width (column, pixel_width); - } -} - -static void -on_variable_changed (PsppireDict *dict, int dict_index, - guint what, const struct variable *oldvar, - PsppireDataSheet *data_sheet) -{ - PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet); - PsppSheetViewColumn *column; - GtkCellRenderer *cell; - struct variable *var; - GList *cells; - char *name; - - g_return_if_fail (data_sheet->data_store != NULL); - g_return_if_fail (dict == data_sheet->data_store->dict); - - - if (what & VAR_TRAIT_DISPLAY_WIDTH) - on_variable_display_width_changed (dict, dict_index, data_sheet); - - column = psppire_data_sheet_find_column_for_variable (data_sheet, - dict_index); - if (column == NULL) - return; - - - var = psppire_dict_get_variable (data_store->dict, dict_index); - g_return_if_fail (var != NULL); - - name = escape_underscores (var_get_name (var)); - if (strcmp (name, pspp_sheet_view_column_get_title (column))) - pspp_sheet_view_column_set_title (column, name); - free (name); - - cells = pspp_sheet_view_column_get_cell_renderers (column); - g_return_if_fail (cells); - cell = cells->data; - g_list_free (cells); - - if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell)) - { - /* Stop editing before we delete and replace the cell renderers. - Otherwise if this column is currently being edited, an eventual call - to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass - that to gtk_cell_renderer_stop_editing(), which causes a critical. - - It's possible that this is a bug in PsppSheetView, and it's possible - that PsppSheetView inherits that from GtkTreeView, but I haven't - investigated yet. */ - pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE); - - add_data_column_cell_renderer (data_sheet, column); - } -} - -static void -on_variable_inserted (PsppireDict *dict, int var_index, - PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - gint base_width, incr_width; - PsppSheetViewColumn *column; - - calc_width_conversion (data_sheet, &base_width, &incr_width); - column = make_data_column (data_sheet, var_index, base_width, incr_width); - pspp_sheet_view_insert_column (sheet_view, column, var_index + 1); -} - -static void -on_variable_deleted (PsppireDict *dict, - const struct variable *var, int case_idx, int width, - PsppireDataSheet *data_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - GList *columns, *iter; - - columns = pspp_sheet_view_get_columns (sheet_view); - for (iter = columns; iter != NULL; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - const struct variable *column_var; - - column_var = g_object_get_data (G_OBJECT (column), "variable"); - if (column_var == var) - pspp_sheet_view_remove_column (sheet_view, column); - } - g_list_free (columns); -} - -static void -psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet) -{ - PsppireDataStore *store = data_sheet->data_store; - - if (store == NULL) - return; - - data_sheet->data_store = NULL; - - g_signal_handlers_disconnect_by_func ( - store, G_CALLBACK (on_backend_changed), data_sheet); - g_signal_handlers_disconnect_by_func ( - store, G_CALLBACK (on_case_inserted), data_sheet); - g_signal_handlers_disconnect_by_func ( - store, G_CALLBACK (on_cases_deleted), data_sheet); - g_signal_handlers_disconnect_by_func ( - store, G_CALLBACK (on_case_change), data_sheet); - - g_signal_handlers_disconnect_by_func ( - store->dict, G_CALLBACK (on_variable_changed), data_sheet); - g_signal_handlers_disconnect_by_func ( - store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet); - g_signal_handlers_disconnect_by_func ( - store->dict, G_CALLBACK (on_variable_inserted), data_sheet); - g_signal_handlers_disconnect_by_func ( - store->dict, G_CALLBACK (on_variable_deleted), data_sheet); - - g_object_unref (store); -} - -void -psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet, - PsppireDataStore *data_store) -{ - psppire_data_sheet_unset_data_store (data_sheet); - - data_sheet->data_store = data_store; - if (data_store != NULL) - { - g_object_ref (data_store); - g_signal_connect (data_store, "backend-changed", - G_CALLBACK (on_backend_changed), data_sheet); - g_signal_connect (data_store, "case-inserted", - G_CALLBACK (on_case_inserted), data_sheet); - g_signal_connect (data_store, "cases-deleted", - G_CALLBACK (on_cases_deleted), data_sheet); - g_signal_connect (data_store, "case-changed", - G_CALLBACK (on_case_change), data_sheet); - - /* XXX it's unclean to hook into the dict this way--what if the dict - changes? As of this writing, though, nothing ever changes the - data_store's dict. */ - g_signal_connect (data_store->dict, "variable-changed", - G_CALLBACK (on_variable_changed), - data_sheet); - g_signal_connect (data_store->dict, "variable-inserted", - G_CALLBACK (on_variable_inserted), data_sheet); - g_signal_connect (data_store->dict, "variable-deleted", - G_CALLBACK (on_variable_deleted), data_sheet); - } - refresh_model (data_sheet); -} - -/* Clipboard stuff */ - -/* A casereader and dictionary holding the data currently in the clip */ -static struct casereader *clip_datasheet = NULL; -static struct dictionary *clip_dict = NULL; - - -static void psppire_data_sheet_update_clipboard (PsppireDataSheet *); - -static gboolean -psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut, - struct casereader **readerp, - struct dictionary **dictp) -{ - struct casewriter *writer ; - PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet); - struct case_map *map = NULL; - struct range_set *rows, *cols; - const struct range_set_node *node; - struct dictionary *dict; - - if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols)) - { - *readerp = NULL; - *dictp = NULL; - return FALSE; - } - - /* Construct clip dictionary. */ - *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict)); - RANGE_SET_FOR_EACH (node, cols) - { - int dict_index; - - for (dict_index = node->start; dict_index < node->end; dict_index++) - { - struct variable *var = dict_get_var (ds->dict->dict, dict_index); - dict_clone_var_assert (dict, var); - } - } - - /* Construct clip data. */ - map = case_map_by_name (ds->dict->dict, dict); - writer = autopaging_writer_create (dict_get_proto (dict)); - RANGE_SET_FOR_EACH (node, rows) - { - unsigned long int row; - - for (row = node->start; row < node->end; row++) - { - struct ccase *old = psppire_data_store_get_case (ds, row); - if (old != NULL) - casewriter_write (writer, case_map_execute (map, old)); - else - casewriter_force_error (writer); - } - } - case_map_destroy (map); - - /* Clear data that we copied out, if we're doing a "cut". */ - if (cut && !casewriter_error (writer)) - { - RANGE_SET_FOR_EACH (node, rows) - { - unsigned long int row; - - for (row = node->start; row < node->end; row++) - { - const struct range_set_node *node2; - - RANGE_SET_FOR_EACH (node2, cols) - { - int dict_index; - - for (dict_index = node2->start; dict_index < node2->end; - dict_index++) - { - struct variable *var; - - var = dict_get_var (ds->dict->dict, dict_index); - psppire_data_store_set_string (ds, "", row, - var, false); - } - } - } - } - } - - range_set_destroy (rows); - range_set_destroy (cols); - - *readerp = casewriter_make_reader (writer); - - return TRUE; -} - -/* Set the clip from the currently selected range in DATA_SHEET. If CUT is - true, clears the original data from DATA_SHEET, otherwise leaves the - original data in-place. */ -static void -psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet, - gboolean cut) -{ - struct casereader *reader; - struct dictionary *dict; - - if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict)) - { - casereader_destroy (clip_datasheet); - dict_destroy (clip_dict); - - clip_datasheet = reader; - clip_dict = dict; - - psppire_data_sheet_update_clipboard (data_sheet); - } -} - -enum { - SELECT_FMT_NULL, - SELECT_FMT_TEXT, - SELECT_FMT_HTML -}; - - -/* Perform data_out for case CC, variable V, appending to STRING */ -static void -data_out_g_string (GString *string, const struct variable *v, - const struct ccase *cc) -{ - const struct fmt_spec *fs = var_get_print_format (v); - const union value *val = case_data (cc, v); - - char *s = data_out (val, var_get_encoding (v), fs); - - g_string_append (string, s); - - g_free (s); -} - -static GString * -clip_to_text (struct casereader *datasheet, struct dictionary *dict) -{ - casenumber r; - GString *string; - - const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet)); - const casenumber case_cnt = casereader_get_case_cnt (datasheet); - const size_t var_cnt = dict_get_var_cnt (dict); - - string = g_string_sized_new (10 * val_cnt * case_cnt); - - for (r = 0 ; r < case_cnt ; ++r ) - { - int c; - struct ccase *cc; - - cc = casereader_peek (datasheet, r); - if (cc == NULL) - { - g_warning ("Clipboard seems to have inexplicably shrunk"); - break; - } - - for (c = 0 ; c < var_cnt ; ++c) - { - const struct variable *v = dict_get_var (dict, c); - data_out_g_string (string, v, cc); - if ( c < val_cnt - 1 ) - g_string_append (string, "\t"); - } - - if ( r < case_cnt) - g_string_append (string, "\n"); - - case_unref (cc); - } - - return string; -} - - -static GString * -clip_to_html (struct casereader *datasheet, struct dictionary *dict) -{ - casenumber r; - GString *string; - - const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet)); - const casenumber case_cnt = casereader_get_case_cnt (datasheet); - const size_t var_cnt = dict_get_var_cnt (dict); - - /* Guestimate the size needed */ - string = g_string_sized_new (80 + 20 * val_cnt * case_cnt); - - g_string_append (string, - "\n"); - - g_string_append (string, "\n"); - for (r = 0 ; r < case_cnt ; ++r ) - { - int c; - struct ccase *cc = casereader_peek (datasheet, r); - if (cc == NULL) - { - g_warning ("Clipboard seems to have inexplicably shrunk"); - break; - } - g_string_append (string, "\n"); - - for (c = 0 ; c < var_cnt ; ++c) - { - const struct variable *v = dict_get_var (dict, c); - g_string_append (string, "\n"); - } - - g_string_append (string, "\n"); - - case_unref (cc); - } - g_string_append (string, "
"); - data_out_g_string (string, v, cc); - g_string_append (string, "
\n"); - - return string; -} - - - -static void -psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data, - guint info, - struct casereader *reader, - struct dictionary *dict) -{ - GString *string = NULL; - - switch (info) - { - case SELECT_FMT_TEXT: - string = clip_to_text (reader, dict); - break; - case SELECT_FMT_HTML: - string = clip_to_html (reader, dict); - break; - default: - g_assert_not_reached (); - } - - gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), - 8, - (const guchar *) string->str, string->len); - - g_string_free (string, TRUE); -} - -static void -psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard, - GtkSelectionData *selection_data, - guint info, - gpointer data) -{ - psppire_data_sheet_clipboard_set (selection_data, info, - clip_datasheet, clip_dict); -} - -static void -psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard, - gpointer data) -{ - dict_destroy (clip_dict); - clip_dict = NULL; - - casereader_destroy (clip_datasheet); - clip_datasheet = NULL; -} - - -static const GtkTargetEntry targets[] = { - { "UTF8_STRING", 0, SELECT_FMT_TEXT }, - { "STRING", 0, SELECT_FMT_TEXT }, - { "TEXT", 0, SELECT_FMT_TEXT }, - { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT }, - { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT }, - { "text/plain", 0, SELECT_FMT_TEXT }, - { "text/html", 0, SELECT_FMT_HTML } -}; - - - -static void -psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet) -{ - GtkClipboard *clipboard = - gtk_widget_get_clipboard (GTK_WIDGET (sheet), - GDK_SELECTION_CLIPBOARD); - - if (!gtk_clipboard_set_with_owner (clipboard, targets, - G_N_ELEMENTS (targets), - psppire_data_sheet_clipboard_get_cb, - psppire_data_sheet_clipboard_clear_cb, - G_OBJECT (sheet))) - psppire_data_sheet_clipboard_clear_cb (clipboard, sheet); -} - -static void -psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet) -{ - struct range_set *rows, *cols; - GtkAction *action; - gboolean enable; - - enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols); - if (enable) - { - range_set_destroy (rows); - range_set_destroy (cols); - } - - action = get_action_assert (data_sheet->builder, "edit_copy"); - gtk_action_set_sensitive (action, enable); - - action = get_action_assert (data_sheet->builder, "edit_cut"); - gtk_action_set_sensitive (action, enable); -} - -static void -psppire_data_sheet_primary_get_cb (GtkClipboard *clipboard, - GtkSelectionData *selection_data, - guint info, - gpointer data) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data); - struct casereader *reader; - struct dictionary *dict; - - if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict)) - { - psppire_data_sheet_clipboard_set (selection_data, info, - reader, dict); - casereader_destroy (reader); - dict_destroy (dict); - } -} - -static void -psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet, - gboolean should_own) -{ - GtkClipboard *clipboard; - GdkDisplay *display; - - display = gtk_widget_get_display (GTK_WIDGET (data_sheet)); - clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY); - g_return_if_fail (clipboard != NULL); - - if (data_sheet->owns_primary_selection && !should_own) - { - data_sheet->owns_primary_selection = FALSE; - gtk_clipboard_clear (clipboard); - } - else if (should_own) - data_sheet->owns_primary_selection = - gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets), - psppire_data_sheet_primary_get_cb, - NULL, G_OBJECT (data_sheet)); -} - -/* A callback for when the clipboard contents have been received. */ -static void -psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard, - GtkSelectionData *sd, - gpointer data) -{ - PsppireDataSheet *data_sheet = data; - PsppireDataStore *store = data_sheet->data_store; - struct range_set *rows, *cols; - gint count = 0; - gint next_row, next_column; - gint first_column; - char *c; - - if ( gtk_selection_data_get_length (sd) < 0 ) - return; - - if ( gtk_selection_data_get_data_type (sd) != gdk_atom_intern ("UTF8_STRING", FALSE)) - return; - - c = (char *) gtk_selection_data_get_data (sd); - - /* Get the starting selected position in the data sheet. (Possibly we should - only paste into the selected range if it's larger than one cell?) */ - if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols)) - return; - next_row = range_set_first (rows)->start; - first_column = next_column = range_set_first (cols)->start; - range_set_destroy (rows); - range_set_destroy (cols); - - g_return_if_fail (next_row >= 0); - g_return_if_fail (next_column >= 0); - - while (count < gtk_selection_data_get_length (sd)) - { - gint row = next_row; - gint column = next_column; - struct variable *var; - char *s = c; - - while (*c != '\t' && *c != '\n' && count < gtk_selection_data_get_length (sd)) - { - c++; - count++; - } - if ( *c == '\t') - { - next_row = row ; - next_column = column + 1; - } - else if ( *c == '\n') - { - next_row = row + 1; - next_column = first_column; - } - *c++ = '\0'; - count++; - - var = psppire_dict_get_variable (store->dict, column); - if (var != NULL) - psppire_data_store_set_string (store, s, row, var, FALSE); - } -} - -static void -psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard, - GdkAtom *atoms, - gint n_atoms, - gpointer data) -{ - GtkAction *action = GTK_ACTION (data); - gboolean compatible_target; - gint i; - - compatible_target = FALSE; - for (i = 0; i < G_N_ELEMENTS (targets); i++) - { - GdkAtom target = gdk_atom_intern (targets[i].target, TRUE); - gint j; - - for (j = 0; j < n_atoms; j++) - if (target == atoms[j]) - { - compatible_target = TRUE; - break; - } - } - - gtk_action_set_sensitive (action, compatible_target); - g_object_unref (action); -} - -static void -on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data) -{ - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data); - GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste"); - - g_object_ref (action); - gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb, - action); -} diff --git a/src/ui/gui/psppire-data-sheet.h b/src/ui/gui/psppire-data-sheet.h index 8497e2c115..05fa69c7b1 100644 --- a/src/ui/gui/psppire-data-sheet.h +++ b/src/ui/gui/psppire-data-sheet.h @@ -17,6 +17,8 @@ #ifndef PSPPIRE_DATA_SHEET_H #define PSPPIRE_DATA_SHEET_H 1 +#error "Do not include this file" + /* PsppireDataSheet is a PsppSheetView that displays the data in a dataset, with one column per variable and one row per case. diff --git a/src/ui/gui/psppire-data-store.c b/src/ui/gui/psppire-data-store.c index 8832e5175e..1441afa56a 100644 --- a/src/ui/gui/psppire-data-store.c +++ b/src/ui/gui/psppire-data-store.c @@ -76,6 +76,90 @@ enum static guint signals [n_SIGNALS]; +static gint +__tree_model_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + PsppireDataStore *store = PSPPIRE_DATA_STORE (tree_model); + + gint n = datasheet_get_n_rows (store->datasheet); + + return n; +} + + +static gint +__tree_model_get_n_columns (GtkTreeModel *tree_model) +{ + PsppireDataStore *store = PSPPIRE_DATA_STORE (tree_model); + + return psppire_dict_get_value_cnt (store->dict); +} + + +static gboolean +__iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + PsppireDataStore *store = PSPPIRE_DATA_STORE (tree_model); + + g_assert (parent == NULL); + + g_return_val_if_fail (store, FALSE); + g_return_val_if_fail (store->datasheet, FALSE); + + if (n >= datasheet_get_n_rows (store->datasheet)) + { + iter->stamp = -1; + iter->user_data = NULL; + return FALSE; + } + + iter->user_data = n; + return TRUE; +} + + + +static void +__get_value (GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + PsppireDataStore *store = PSPPIRE_DATA_STORE (tree_model); + + g_value_init (value, G_TYPE_DOUBLE); + + gint row = GPOINTER_TO_INT (iter->user_data); + + struct ccase *cc = datasheet_get_row (store->datasheet, row); + + g_value_set_double (value, case_data_idx (cc, column)->f); + case_unref (cc); +} + + +static void +__tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = NULL; + iface->get_n_columns = __tree_model_get_n_columns ; + iface->get_column_type = NULL; + iface->get_iter = NULL; + iface->iter_next = NULL; + iface->get_path = NULL; + iface->get_value = __get_value; + + iface->iter_children = NULL; + iface->iter_has_child = NULL; + iface->iter_n_children = __tree_model_iter_n_children; + iface->iter_nth_child = __iter_nth_child; + iface->iter_parent = NULL; +} + GType psppire_data_store_get_type (void) @@ -97,9 +181,18 @@ psppire_data_store_get_type (void) (GInstanceInitFunc) psppire_data_store_init, }; + static const GInterfaceInfo tree_model_info = { + (GInterfaceInitFunc) __tree_model_init, + NULL, + NULL + }; + data_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDataStore", &data_store_info, 0); + + g_type_add_interface_static (data_store_type, GTK_TYPE_TREE_MODEL, + &tree_model_info); } return data_store_type; @@ -298,6 +391,9 @@ psppire_data_store_set_reader (PsppireDataStore *ds, ds->datasheet = datasheet_create (reader); + g_signal_emit_by_name (ds, "notify", 0, 0); + g_print ("Datasheet row count %d\n", datasheet_get_n_rows (ds->datasheet)); + if ( ds->dict ) for (i = 0 ; i < n_dict_signals; ++i ) { diff --git a/src/ui/gui/psppire-dict.c b/src/ui/gui/psppire-dict.c index b88bbe7766..cedb742583 100644 --- a/src/ui/gui/psppire-dict.c +++ b/src/ui/gui/psppire-dict.c @@ -33,6 +33,9 @@ #include "ui/gui/psppire-marshal.h" #include "ui/gui/psppire-var-ptr.h" +#include "ui/gui/efficient-sheet/jmd-datum.h" + + #include #define _(msgid) gettext (msgid) #define N_(msgid) msgid @@ -59,6 +62,53 @@ static void psppire_dict_dispose (GObject *object); static void dictionary_tree_model_init (GtkTreeModelIface *iface); + +static guint +gni (GListModel *list) +{ + PsppireDict *dict = PSPPIRE_DICT (list); + + return psppire_dict_get_var_cnt (dict); +} + +static GType +git (GListModel *list) +{ + return JMD_TYPE_DATUM; +} + +static gpointer +gi (GListModel *list, guint id) +{ + JmdDatum *gd = JMD_DATUM (g_object_new (JMD_TYPE_DATUM, NULL)); + + PsppireDict *dict = PSPPIRE_DICT (list); + + if (id >= psppire_dict_get_var_cnt (dict)) + { + gd->text = g_strdup (_("Var")); + } + else + { + const struct variable *v = psppire_dict_get_variable (dict, id); + + gd->text = g_strdup (var_get_name (v)); + gd->label = g_strdup (var_get_label (v)); + } + + return gd; +} + + +static void +jmd_init_iface (GListModelInterface *iface) +{ + iface->get_n_items = gni; + iface->get_item = gi; + iface->get_item_type = git; +} + + /* --- variables --- */ static GObjectClass *parent_class = NULL; @@ -94,12 +144,21 @@ psppire_dict_get_type (void) NULL }; + static const GInterfaceInfo list_model_info = { + (GInterfaceInitFunc) jmd_init_iface, + NULL, + NULL + }; + object_type = g_type_register_static (G_TYPE_OBJECT, "PsppireDict", &object_info, 0); - + g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL, &tree_model_info); + + g_type_add_interface_static (object_type, G_TYPE_LIST_MODEL, + &list_model_info); } return object_type;