From: John Darrington Date: Sun, 10 Jul 2016 05:47:34 +0000 (+0200) Subject: Merge remote-tracking branch 'origin/master' into sheet X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f9b2322d7b0662bd313d9c63450638c39b88be70;hp=5ea81ae302c62daadda211d5ce777091ad608c1b;p=pspp Merge remote-tracking branch 'origin/master' into sheet --- diff --git a/configure.ac b/configure.ac index 417a6c2e35..4891c906f0 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/data/variable.c b/src/data/variable.c index 6a00d014a1..42a07ee8bb 100644 --- a/src/data/variable.c +++ b/src/data/variable.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,6 +42,42 @@ #include "gettext.h" #define _(msgid) gettext (msgid) +#define N_(msgid) (msgid) + +/* This should follow the definition in Gtk */ +typedef struct +{ + int value; + const char *name; + const char *label; +} GEnumValue; + +const GEnumValue align[] = + { + {ALIGN_LEFT, "left", N_("Left")}, + {ALIGN_RIGHT, "right", N_("Right")}, + {ALIGN_CENTRE, "center", N_("Center")}, + {0,0,0} + }; + +const GEnumValue measure[] = + { + {MEASURE_NOMINAL, "nominal", N_("Nominal")}, + {MEASURE_ORDINAL, "ordinal", N_("Ordinal")}, + {MEASURE_SCALE, "scale", N_("Scale")}, + {0,0,0} + }; + +const GEnumValue role[] = + { + {ROLE_INPUT, "input", N_("Input")}, + {ROLE_TARGET, "output", N_("Output")}, + {ROLE_BOTH, "both", N_("Both")}, + {ROLE_NONE, "none", N_("None")}, + {ROLE_PARTITION, "partition", N_("Partition")}, + {ROLE_SPLIT, "split", N_("Split")}, + {0,0,0} + }; /* A variable. */ struct variable @@ -771,20 +807,8 @@ measure_is_valid (enum measure m) const char * measure_to_string (enum measure m) { - switch (m) - { - case MEASURE_NOMINAL: - return _("Nominal"); - - case MEASURE_ORDINAL: - return _("Ordinal"); - - case MEASURE_SCALE: - return _("Scale"); - - default: - return "Invalid"; - } + assert (m == measure[m].value); + return gettext (measure[m].label); } /* Returns a string version of measurement level M, for use in PSPP command @@ -866,31 +890,10 @@ var_role_is_valid (enum var_role role) /* Returns a string version of ROLE, for display to a user. */ const char * -var_role_to_string (enum var_role role) +var_role_to_string (enum var_role r) { - switch (role) - { - case ROLE_INPUT: - return _("Input"); - - case ROLE_TARGET: - return _("Output"); - - case ROLE_BOTH: - return _("Both"); - - case ROLE_NONE: - return _("None"); - - case ROLE_PARTITION: - return _("Partition"); - - case ROLE_SPLIT: - return _("Split"); - - default: - return "Invalid"; - } + assert (r == role[r].value); + return gettext (role[r].label); } /* Returns a string version of ROLE, for use in PSPP comamnd syntax. */ @@ -996,20 +999,8 @@ alignment_is_valid (enum alignment a) const char * alignment_to_string (enum alignment a) { - switch (a) - { - case ALIGN_LEFT: - return _("Left"); - - case ALIGN_RIGHT: - return _("Right"); - - case ALIGN_CENTRE: - return _("Center"); - - default: - return "Invalid"; - } + assert (a == align[a].value); + return gettext (align[a].label); } /* Returns a string version of alignment A, for use in PSPP command syntax. */ diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index 0f0ea34662..1155cf70db 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -69,6 +69,7 @@ EXTRA_DIST += \ src/ui/gui/marshaller-list \ src/ui/gui/pspplogo.svg +src_ui_gui_psppire_CPPFLAGS= if HAVE_GUI bin_PROGRAMS += src/ui/gui/psppire @@ -306,8 +307,9 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/psppire-value-entry.h \ src/ui/gui/psppire-var-ptr.c \ src/ui/gui/psppire-var-ptr.h \ - src/ui/gui/psppire-var-sheet.c \ src/ui/gui/psppire-var-sheet.h \ + src/ui/gui/psppire-var-sheet-header.h \ + src/ui/gui/psppire-var-sheet-header.c \ src/ui/gui/psppire-vbuttonbox.h \ src/ui/gui/psppire-window.c \ src/ui/gui/psppire-window.h \ @@ -387,6 +389,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.mk endif HAVE_GUI #ensure the installcheck passes even if there is no X server available @@ -394,7 +397,7 @@ installcheck-local: DISPLAY=/invalid/port $(MAKE) $(AM_MAKEFLAGS) installcheck-binPROGRAMS # wrapper -src_ui_gui_psppire_CPPFLAGS = $(AM_CPPFLAGS) -Isrc/ui/gui/include +src_ui_gui_psppire_CPPFLAGS += $(AM_CPPFLAGS) -Isrc/ui/gui/include BUILT_SOURCES += src/ui/gui/include/gtk/gtk.h src/ui/gui/include/gtk/gtk.h: src/ui/gui/include/gtk/gtk.in.h @$(MKDIR_P) src/ui/gui/include/gtk diff --git a/src/ui/gui/efficient-sheet.mk b/src/ui/gui/efficient-sheet.mk new file mode 100644 index 0000000000..c2512ad73c --- /dev/null +++ b/src/ui/gui/efficient-sheet.mk @@ -0,0 +1,29 @@ +src_ui_gui_psppire_SOURCES += \ + src/ui/gui/efficient-sheet/jmd-axis-model.c \ + src/ui/gui/efficient-sheet/jmd-constraint.c \ + src/ui/gui/efficient-sheet/jmd-sheet.c \ + src/ui/gui/efficient-sheet/jmd-sheet-axis.c \ + src/ui/gui/efficient-sheet/jmd-sheet-body.c \ + src/ui/gui/efficient-sheet/jmd-sheet-single.c \ + src/ui/gui/efficient-sheet/jmd-datum.c \ + src/ui/gui/efficient-sheet/jmd-cell.c + + +nodist_src_ui_gui_psppire_SOURCES += \ + src/ui/gui/efficient-sheet/jmd-marshaller.c \ + src/ui/gui/efficient-sheet/jmd-marshaller.h + +src_ui_gui_psppire_CPPFLAGS+=-Isrc/ui/gui/efficient-sheet + + + +BUILT_SOURCES += \ + src/ui/gui/efficient-sheet/jmd-marshaller.c \ + src/ui/gui/efficient-sheet/jmd-marshaller.h + +src/ui/gui/efficient-sheet/jmd-marshaller.c: src/ui/gui/efficient-sheet/marshall-list + glib-genmarshal --body --prefix=jmd_cclosure_marshal $< > $@ + +src/ui/gui/efficient-sheet/jmd-marshaller.h: src/ui/gui/efficient-sheet/marshall-list + glib-genmarshal --header --prefix=jmd_cclosure_marshal $< > $@ + 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/marshaller-list b/src/ui/gui/marshaller-list index 0f38e6fc2e..5b6c0e5b58 100644 --- a/src/ui/gui/marshaller-list +++ b/src/ui/gui/marshaller-list @@ -12,3 +12,4 @@ VOID:INT,INT VOID:OBJECT,OBJECT VOID:POINTER,INT,INT VOID:INT,UINT,POINTER +VOID:UINT,UINT,UINT diff --git a/src/ui/gui/psppire-data-editor.c b/src/ui/gui/psppire-data-editor.c index 18fd5e8e8d..b643acf046 100644 --- a/src/ui/gui/psppire-data-editor.c +++ b/src/ui/gui/psppire-data-editor.c @@ -27,21 +27,107 @@ #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/psppire-var-sheet-header.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 GtkCellRenderer * +create_spin_renderer (GType type) +{ + GtkCellRenderer *r = gtk_cell_renderer_spin_new (); + + GtkAdjustment *adj = gtk_adjustment_new (0, + 0, G_MAXDOUBLE, + 1, 1, + 0); + g_object_set (r, + "adjustment", adj, + NULL); + + return r; +} + +static GtkCellRenderer * +create_combo_renderer (GType type) +{ + GtkListStore *list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING); + + GEnumClass *ec = g_type_class_ref (type); + + const GEnumValue *ev ; + for (ev = ec->values; ev->value_name; ++ev) + { + GtkTreeIter iter; + + gtk_list_store_append (list_store, &iter); + + gtk_list_store_set (list_store, &iter, + 0, ev->value, + 1, gettext (ev->value_nick), + -1); + } + + GtkCellRenderer *r = gtk_cell_renderer_combo_new (); + + g_object_set (r, + "model", list_store, + "text-column", 1, + "has-entry", TRUE, + NULL); + + return r; +} + +GtkCellRenderer *xx ; +GtkCellRenderer *column_width_renderer ; +GtkCellRenderer *measure_renderer ; +GtkCellRenderer *alignment_renderer ; + + + +static GtkCellRenderer * +select_renderer_func (gint col, gint row, GType type) +{ + if (!xx) + xx = create_spin_renderer (type); + + if (col == DICT_TVM_COL_ROLE && !column_width_renderer) + column_width_renderer = create_combo_renderer (type); + + if (col == DICT_TVM_COL_MEASURE && !measure_renderer) + measure_renderer = create_combo_renderer (type); + + if (col == DICT_TVM_COL_ALIGNMENT && !alignment_renderer) + alignment_renderer = create_combo_renderer (type); + + switch (col) + { + case DICT_TVM_COL_WIDTH: + case DICT_TVM_COL_DECIMAL: + case DICT_TVM_COL_COLUMNS: + return xx; + + case DICT_TVM_COL_ALIGNMENT: + return alignment_renderer; + + case DICT_TVM_COL_MEASURE: + return measure_renderer; + + case DICT_TVM_COL_ROLE: + return column_width_renderer; + } + + return NULL; +} + static void psppire_data_editor_class_init (PsppireDataEditorClass *klass); static void psppire_data_editor_init (PsppireDataEditor *de); @@ -127,13 +213,52 @@ enum static void psppire_data_editor_refresh_model (PsppireDataEditor *de) { - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (de->var_sheet); - PsppireDataSheet *data_sheet; - int i; +} + +static void +change_var_property (PsppireDict *dict, gint col, gint row, GValue *value) +{ + /* Return the IDXth variable */ + struct variable *var = psppire_dict_get_variable (dict, row); + + switch (col) + { + case DICT_TVM_COL_NAME: + dict_rename_var (dict->dict, var, g_value_get_string (value)); + break; + case DICT_TVM_COL_LABEL: + var_set_label (var, g_value_get_string (value)); + break; + case DICT_TVM_COL_COLUMNS: + var_set_display_width (var, g_value_get_int (value)); + break; + case DICT_TVM_COL_MEASURE: + var_set_measure (var, g_value_get_enum (value)); + break; + case DICT_TVM_COL_ALIGNMENT: + var_set_alignment (var, g_value_get_enum (value)); + break; + case DICT_TVM_COL_ROLE: + var_set_role (var, g_value_get_enum (value)); + break; + default: + g_message ("Changing col %d of var sheet not yet supported", col); + break; + } +} + +static void +change_data_value (PsppireDataStore *store, gint col, gint row, GValue *value) +{ + const struct variable *var = psppire_dict_get_variable (store->dict, col); - 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); + union value v; + value_init (&v, var_get_width (var)); + v.f = g_value_get_double (value); + + psppire_data_store_set_value (store, row, var, &v); + + value_destroy (&v, var_get_width (var)); } static void @@ -143,8 +268,6 @@ psppire_data_editor_set_property (GObject *object, GParamSpec *pspec) { PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object); - PsppireDataSheet *data_sheet; - int i; switch (prop_id) { @@ -162,8 +285,14 @@ 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_sheet, "value-changed", + G_CALLBACK (change_data_value), de->data_store); + g_signal_connect_swapped (de->data_store, "case-changed", G_CALLBACK (refresh_entry), de); @@ -174,13 +303,13 @@ psppire_data_editor_set_property (GObject *object, de->dict = g_value_get_pointer (value); g_object_ref (de->dict); - psppire_var_sheet_set_dictionary (PSPPIRE_VAR_SHEET (de->var_sheet), - de->dict); + g_object_set (de->data_sheet, "hmodel", de->dict, NULL); + g_object_set (de->var_sheet, "data-model", de->dict, NULL); + g_signal_connect_swapped (de->var_sheet, "value-changed", + G_CALLBACK (change_var_property), 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 +338,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)); @@ -315,390 +441,95 @@ psppire_data_editor_class_init (PsppireDataEditorClass *klass) ui_manager_spec); } -static gboolean -on_data_sheet_var_double_clicked (PsppireDataSheet *data_sheet, - gint dict_index, - PsppireDataEditor *de) + +static void +on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index, + PsppireDataEditor *de) { gtk_notebook_set_current_page (GTK_NOTEBOOK (de), - PSPPIRE_DATA_EDITOR_VARIABLE_VIEW); - - psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet), - dict_index); + PSPPIRE_DATA_EDITOR_DATA_VIEW); - return TRUE; + jmd_sheet_scroll_to (JMD_SHEET (de->data_sheet), dict_index, -1); } -static gboolean -on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index, +static void +on_data_sheet_var_double_clicked (JmdSheet *data_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); + PSPPIRE_DATA_EDITOR_VARIABLE_VIEW); - return TRUE; + jmd_sheet_scroll_to (JMD_SHEET (de->var_sheet), -1, dict_index); } + /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently active cell or cells. */ 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); + g_print ("%s\n", __FUNCTION__); } 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 -on_data_sheet_selection_changed (PsppSheetSelection *selection, - PsppireDataEditor *de) -{ - /* In a split view, ensure that only a single data sheet has a nonempty - selection. */ - if (de->split - && 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) +/* Called when the active cell or the selection in the data sheet changes */ +static void +on_data_selection_change (PsppireDataEditor *de, JmdRange *sel) { - 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; + gchar *ref_cell_text = 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]); + gint n_cases = abs (sel->end_y - sel->start_y) + 1; + gint n_vars = abs (sel->end_x - sel->start_x) + 1; - 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 + if (n_cases == 1 && n_vars == 1) { - 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++) + /* A single cell is selected */ + const struct variable *var = psppire_dict_get_variable (de->dict, sel->start_x); + + ref_cell_text = g_strdup_printf (_("%d : %s"), + sel->start_y + 1, var_get_name (var)); + } + else { - 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 (); - } + 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); } - - /* 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); + + gtk_label_set_label (GTK_LABEL (de->cell_ref_label), + ref_cell_text ? ref_cell_text : ""); + + g_free (ref_cell_text); } + static void set_font_recursively (GtkWidget *w, gpointer data); static void psppire_data_editor_init (PsppireDataEditor *de) { - GtkWidget *var_sheet_scroller; GtkWidget *hbox; gchar *fontname = NULL; @@ -721,33 +552,46 @@ 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, NULL); + GtkWidget *data_button = jmd_sheet_get_button (JMD_SHEET (de->data_sheet)); + gtk_button_set_label (GTK_BUTTON (data_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); + + g_signal_connect_swapped (de->data_sheet, "selection-changed", + G_CALLBACK (on_data_selection_change), de); + gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox, gtk_label_new_with_mnemonic (_("Data View"))); 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); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (var_sheet_scroller), - 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, + de->var_sheet = g_object_new (JMD_TYPE_SHEET, NULL); + + PsppireVarSheetHeader *vsh = g_object_new (PSPPIRE_TYPE_VAR_SHEET_HEADER, NULL); + + g_object_set (de->var_sheet, + "hmodel", vsh, + "select-renderer-func", select_renderer_func, + NULL); + + + GtkWidget *var_button = jmd_sheet_get_button (JMD_SHEET (de->var_sheet)); + gtk_button_set_label (GTK_BUTTON (var_button), _("Variable")); + + gtk_notebook_append_page (GTK_NOTEBOOK (de), de->var_sheet, gtk_label_new_with_mnemonic (_("Variable View"))); - g_signal_connect (de->var_sheet, "var-double-clicked", + gtk_widget_show_all (de->var_sheet); + + g_signal_connect (de->var_sheet, "row-header-double-clicked", G_CALLBACK (on_var_sheet_var_double_clicked), de); + g_signal_connect (de->data_sheet, "column-header-double-clicked", + G_CALLBACK (on_data_sheet_var_double_clicked), de); + g_object_set (de, "can-focus", FALSE, NULL); if (psppire_conf_get_string (psppire_conf_new (), @@ -778,15 +622,11 @@ 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); } @@ -825,37 +665,13 @@ psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_ void psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split) { - GtkTreeViewGridLines grid_lines; - gboolean labels; - if (split == de->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,57 +687,8 @@ 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), - dict_index); - break; - } } -/* 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 +706,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,18 +713,9 @@ 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: - ui_manager = psppire_var_sheet_get_ui_manager ( - PSPPIRE_VAR_SHEET (de->var_sheet)); break; default: 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..74677259f7 100644 --- a/src/ui/gui/psppire-data-store.c +++ b/src/ui/gui/psppire-data-store.c @@ -67,7 +67,7 @@ static GObjectClass *parent_class = NULL; enum { - BACKEND_CHANGED, + ITEMS_CHANGED, CASES_DELETED, CASE_INSERTED, CASE_CHANGED, @@ -76,6 +76,122 @@ 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 GtkTreeModelFlags +__tree_model_get_flags (GtkTreeModel *model) +{ + g_return_val_if_fail (PSPPIRE_IS_DATA_STORE (model), (GtkTreeModelFlags) 0); + + return GTK_TREE_MODEL_LIST_ONLY; +} + +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); + + const struct variable *variable = psppire_dict_get_variable (store->dict, column); + + if (var_is_numeric (variable)) + g_value_init (value, G_TYPE_DOUBLE); + else + g_value_init (value, G_TYPE_STRING); + + gint row = GPOINTER_TO_INT (iter->user_data); + + struct ccase *cc = datasheet_get_row (store->datasheet, row); + + if (var_is_numeric (variable)) + g_value_set_double (value, case_data_idx (cc, column)->f); + else + { + const gchar *ss = value_str (case_data_idx (cc, column), + var_get_width (variable)); + g_value_set_string (value, ss); + } + case_unref (cc); +} + + +static GType +__get_type (GtkTreeModel *tree_model, gint idx) +{ + PsppireDataStore *store = PSPPIRE_DATA_STORE (tree_model); + + const struct variable *variable = psppire_dict_get_variable (store->dict, idx); + + if (var_is_numeric (variable)) + return G_TYPE_DOUBLE; + + return G_TYPE_STRING; +} + +static void +__tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = __tree_model_get_flags; + iface->get_n_columns = __tree_model_get_n_columns ; + iface->get_column_type = __get_type; + 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 +213,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; @@ -117,15 +242,18 @@ psppire_data_store_class_init (PsppireDataStoreClass *class) object_class->finalize = psppire_data_store_finalize; object_class->dispose = psppire_data_store_dispose; - signals [BACKEND_CHANGED] = - g_signal_new ("backend-changed", + signals [ITEMS_CHANGED] = + g_signal_new ("changed", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, + psppire_marshal_VOID__UINT_UINT_UINT, G_TYPE_NONE, - 0); + 3, + G_TYPE_UINT, + G_TYPE_UINT, + G_TYPE_UINT); signals [CASE_INSERTED] = g_signal_new ("case-inserted", @@ -292,12 +420,17 @@ psppire_data_store_set_reader (PsppireDataStore *ds, struct casereader *reader) { gint i; - + gint old_n = 0; if ( ds->datasheet) - datasheet_destroy (ds->datasheet); + { + old_n = datasheet_get_n_rows (ds->datasheet); + datasheet_destroy (ds->datasheet); + } ds->datasheet = datasheet_create (reader); + gint new_n = datasheet_get_n_rows (ds->datasheet); + if ( ds->dict ) for (i = 0 ; i < n_dict_signals; ++i ) { @@ -308,7 +441,7 @@ psppire_data_store_set_reader (PsppireDataStore *ds, } } - g_signal_emit (ds, signals[BACKEND_CHANGED], 0); + g_signal_emit (ds, signals[ITEMS_CHANGED], 0, 0, old_n, new_n); } diff --git a/src/ui/gui/psppire-data-window.c b/src/ui/gui/psppire-data-window.c index 7917274da5..c2a27c04f4 100644 --- a/src/ui/gui/psppire-data-window.c +++ b/src/ui/gui/psppire-data-window.c @@ -1009,7 +1009,7 @@ psppire_data_window_finish_init (PsppireDataWindow *de, G_CALLBACK (on_split_change), de); - g_signal_connect_swapped (de->dict, "backend-changed", + g_signal_connect_swapped (de->dict, "changed", G_CALLBACK (enable_save), de); g_signal_connect_swapped (de->dict, "variable-inserted", G_CALLBACK (enable_save), de); diff --git a/src/ui/gui/psppire-dict.c b/src/ui/gui/psppire-dict.c index b88bbe7766..53ba83aa1d 100644 --- a/src/ui/gui/psppire-dict.c +++ b/src/ui/gui/psppire-dict.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2004, 2006, 2007, 2009, 2010, 2011, 2012 Free Software Foundation + Copyright (C) 2004, 2006, 2007, 2009, 2010, 2011, 2012, 2016 Free Software Foundation This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,12 +33,23 @@ #include "ui/gui/psppire-marshal.h" #include "ui/gui/psppire-var-ptr.h" +#include "ui/gui/efficient-sheet/jmd-datum.h" + +#include + #include #define _(msgid) gettext (msgid) #define N_(msgid) msgid + + +GType align_enum_type; +GType measure_enum_type; +GType role_enum_type; + + enum { - BACKEND_CHANGED, + ITEMS_CHANGED, VARIABLE_CHANGED, VARIABLE_INSERTED, @@ -59,6 +70,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 +152,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; @@ -114,16 +181,19 @@ psppire_dict_class_init (PsppireDictClass *class) parent_class = g_type_class_peek_parent (class); object_class->dispose = psppire_dict_dispose; - - signals [BACKEND_CHANGED] = - g_signal_new ("backend-changed", + + signals [ITEMS_CHANGED] = + g_signal_new ("changed", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, + psppire_marshal_VOID__UINT_UINT_UINT, G_TYPE_NONE, - 0); + 3, + G_TYPE_UINT, + G_TYPE_UINT, + G_TYPE_UINT); signals [VARIABLE_CHANGED] = @@ -221,7 +291,10 @@ addcb (struct dictionary *d, int idx, void *pd) PsppireDict *dict = PSPPIRE_DICT (pd); if ( ! dict->disable_insert_signal) - g_signal_emit (dict, signals [VARIABLE_INSERTED], 0, idx); + { + g_signal_emit (dict, signals [VARIABLE_INSERTED], 0, idx); + g_signal_emit (dict, signals [ITEMS_CHANGED], 0, idx, 1, 1); + } } static void @@ -230,12 +303,14 @@ delcb (struct dictionary *d, const struct variable *var, { g_signal_emit (pd, signals [VARIABLE_DELETED], 0, var, dict_idx, case_idx); + g_signal_emit (pd, signals [ITEMS_CHANGED], 0, dict_idx, 1, 0); } static void mutcb (struct dictionary *d, int idx, unsigned int what, const struct variable *oldvar, void *pd) { g_signal_emit (pd, signals [VARIABLE_CHANGED], 0, idx, what, oldvar); + g_signal_emit (pd, signals [ITEMS_CHANGED], 0, idx, 1, 1); } static void @@ -284,7 +359,7 @@ psppire_dict_new_from_dict (struct dictionary *d) { PsppireDict *new_dict = g_object_new (PSPPIRE_TYPE_DICT, NULL); new_dict->dict = d; - + dict_set_callbacks (new_dict->dict, &gui_callbacks, new_dict); return new_dict; @@ -296,6 +371,9 @@ psppire_dict_replace_dictionary (PsppireDict *dict, struct dictionary *d) { struct variable *var = dict_get_weight (d); + guint old_n = dict_get_var_cnt (dict->dict); + guint new_n = dict_get_var_cnt (d); + dict->dict = d; weight_changed_callback (d, var ? var_get_dict_index (var) : -1, dict); @@ -307,7 +385,7 @@ psppire_dict_replace_dictionary (PsppireDict *dict, struct dictionary *d) dict_set_callbacks (dict->dict, &gui_callbacks, dict); - g_signal_emit (dict, signals [BACKEND_CHANGED], 0); + g_signal_emit (dict, signals [ITEMS_CHANGED], 0, 0, old_n, new_n); } @@ -371,7 +449,8 @@ psppire_dict_insert_variable (PsppireDict *d, gint idx, const gchar *name) d->disable_insert_signal = FALSE; g_signal_emit (d, signals[VARIABLE_INSERTED], 0, idx); - + g_signal_emit (d, signals [ITEMS_CHANGED], 0, idx, 0, 1); + return var; } @@ -617,24 +696,34 @@ tree_model_column_type (GtkTreeModel *model, gint index) { g_return_val_if_fail (PSPPIRE_IS_DICT (model), (GType) 0); + GType t = 0; + switch (index) { case DICT_TVM_COL_NAME: - return G_TYPE_STRING; + case DICT_TVM_COL_LABEL: + t = G_TYPE_STRING; + break; + case DICT_TVM_COL_DECIMAL: + case DICT_TVM_COL_WIDTH: + case DICT_TVM_COL_COLUMNS: + t = G_TYPE_INT; break; case DICT_TVM_COL_VAR: - return PSPPIRE_VAR_PTR_TYPE; + t = PSPPIRE_VAR_PTR_TYPE; break; - case DICT_TVM_COL_LABEL: - return G_TYPE_STRING; + case DICT_TVM_COL_ALIGNMENT: + t = align_enum_type; break; - default: - g_return_val_if_reached ((GType)0); + case DICT_TVM_COL_MEASURE: + t = measure_enum_type; + break; + case DICT_TVM_COL_ROLE: + t = role_enum_type; break; } - g_assert_not_reached (); - return ((GType)0); + return t; } static gboolean @@ -722,6 +811,7 @@ tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter) return path; } +const struct fmt_spec *var_get_write_format (const struct variable *); static void tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, @@ -732,22 +822,51 @@ tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, g_return_if_fail (iter->stamp == dict->stamp); - var = iter->user_data; + var = iter->user_data; + const struct fmt_spec *fs = var_get_write_format (var); + switch (column) { case DICT_TVM_COL_NAME: - { - g_value_init (value, G_TYPE_STRING); - g_value_set_string (value, var_get_name (var)); - } + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, var_get_name (var)); + break; + case DICT_TVM_COL_WIDTH: + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, fs->w); + break; + case DICT_TVM_COL_DECIMAL: + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, fs->d); + break; + case DICT_TVM_COL_LABEL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, var_get_label (var)); + break; + case DICT_TVM_COL_COLUMNS: + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, var_get_display_width (var)); + break; + case DICT_TVM_COL_ALIGNMENT: + g_value_init (value, align_enum_type); + g_value_set_enum (value, var_get_alignment (var)); + break; + case DICT_TVM_COL_MEASURE: + g_value_init (value, measure_enum_type); + g_value_set_enum (value, var_get_measure (var)); + break; + case DICT_TVM_COL_ROLE: + g_value_init (value, role_enum_type); + g_value_set_enum (value, var_get_role (var)); break; case DICT_TVM_COL_VAR: g_value_init (value, PSPPIRE_VAR_PTR_TYPE); g_value_set_boxed (value, var); break; default: - g_return_if_reached (); + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, "????"); break; } } @@ -766,7 +885,7 @@ tree_model_n_children (GtkTreeModel *model, { PsppireDict *dict = PSPPIRE_DICT (model); - if ( iter == NULL ) + if (iter == NULL) return psppire_dict_get_var_cnt (dict); return 0; diff --git a/src/ui/gui/psppire-dict.h b/src/ui/gui/psppire-dict.h index ecd5de063f..4d78807cba 100644 --- a/src/ui/gui/psppire-dict.h +++ b/src/ui/gui/psppire-dict.h @@ -42,7 +42,19 @@ G_BEGIN_DECLS typedef struct _PsppireDict PsppireDict; typedef struct _PsppireDictClass PsppireDictClass; -enum {DICT_TVM_COL_NAME=0, DICT_TVM_COL_VAR, DICT_TVM_COL_LABEL, n_DICT_COLS} ; +enum {DICT_TVM_COL_NAME=0, + DICT_TVM_COL_TYPE, + DICT_TVM_COL_WIDTH, + DICT_TVM_COL_DECIMAL, + DICT_TVM_COL_LABEL, + DICT_TVM_COL_VALUE_LABELS, + DICT_TVM_COL_MISSING_VALUES, + DICT_TVM_COL_COLUMNS, + DICT_TVM_COL_ALIGNMENT, + DICT_TVM_COL_MEASURE, + DICT_TVM_COL_ROLE, + DICT_TVM_COL_VAR, + n_DICT_COLS} ; struct _PsppireDict { diff --git a/src/ui/gui/psppire-import-assistant.c b/src/ui/gui/psppire-import-assistant.c index 1b6cf570af..d3131a3a2c 100644 --- a/src/ui/gui/psppire-import-assistant.c +++ b/src/ui/gui/psppire-import-assistant.c @@ -1951,200 +1951,6 @@ on_variable_change (PsppireDict *dict, int dict_idx, static void prepare_formats_page (PsppireImportAssistant *ia) { - PsppireDict *psppire_dict = NULL; - PsppireVarSheet *var_sheet; - GtkBin *vars_scroller; - GtkWidget *old_var_sheet; - - - push_watch_cursor (ia); - - if (ia->spreadsheet == NULL) - { - struct fmt_guesser *fg; - unsigned long int number = 0; - size_t column_idx; - - - ia->dict = dict_create (get_default_encoding ()); - fg = fmt_guesser_create (); - for (column_idx = 0; column_idx < ia->column_cnt; column_idx++) - { - struct variable *modified_var = - (column_idx < ia->modified_var_cnt ? ia->modified_vars[column_idx] : NULL); - - if (modified_var == NULL) - { - struct column *column = &ia->columns[column_idx]; - struct variable *var; - struct fmt_spec format; - char *name; - size_t row; - - /* Choose variable name. */ - name = dict_make_unique_var_name (ia->dict, column->name, &number); - - /* Choose variable format. */ - fmt_guesser_clear (fg); - for (row = ia->skip_lines; row < ia->line_cnt; row++) - fmt_guesser_add (fg, column->contents[row]); - fmt_guesser_guess (fg, &format); - fmt_fix_input (&format); - - /* Create variable. */ - var = dict_create_var_assert (ia->dict, name, fmt_var_width (&format)); - var_set_both_formats (var, &format); - - free (name); - } - else - { - char *name; - - name = dict_make_unique_var_name (ia->dict, var_get_name (modified_var), - &number); - dict_clone_var_as_assert (ia->dict, modified_var, name); - free (name); - } - } - fmt_guesser_destroy (fg); - } - else - { - int row_start = -1; - int row_stop = -1; - int col_start = -1; - int col_stop = -1; - - GtkBuilder *builder = ia->builder; - - struct casereader *reader = NULL; - - GtkWidget *readnames_checkbox = get_widget_assert (builder, "readnames-checkbox"); - GtkWidget *range_entry = get_widget_assert (builder, "cell-range-entry"); - const gchar *range = gtk_entry_get_text (GTK_ENTRY (range_entry)); - GtkWidget *combo_box = get_widget_assert (builder, "sheet-entry"); - - gint num = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)); - - struct spreadsheet_read_options sro; - - sro.sheet_name = NULL; - sro.cell_range = NULL; - sro.sheet_index = num + 1; - - if ( convert_cell_ref (range, &col_start, &row_start, &col_stop, &row_stop)) - { - sro.cell_range = g_strdup (range); - } - - sro.read_names = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (readnames_checkbox)); - sro.asw = -1; - - switch (ia->spreadsheet->type) - { - case SPREADSHEET_ODS: - case SPREADSHEET_GNUMERIC: - { - reader = spreadsheet_make_reader (ia->spreadsheet, &sro); - ia->dict = dict_clone (ia->spreadsheet->dict); - } - break; - default: - g_assert_not_reached (); - break; - } - g_free (sro.cell_range); - - if (reader && ia->dict) - { - struct ccase *c; - int col; - - ia->column_cnt = dict_get_var_cnt (ia->dict); - ia->columns = xcalloc (ia->column_cnt, sizeof (*ia->columns)); - for (col = 0; col < ia->column_cnt ; ++col) - { - const struct variable *var = dict_get_var (ia->dict, col); - ia->columns[col].name = xstrdup (var_get_name (var)); - ia->columns[col].contents = NULL; - } - - casenumber rows = 0; - for (; (c = casereader_read (reader)) != NULL; case_unref (c)) - { - rows++; - for (col = 0; col < ia->column_cnt ; ++col) - { - char *ss; - const struct variable *var = dict_get_var (ia->dict, col); - - ia->columns[col].contents = xrealloc (ia->columns[col].contents, - sizeof (struct substring) * rows); - - ss = data_out (case_data (c, var), dict_get_encoding (ia->dict), - var_get_print_format (var)); - - ia->columns[col].contents[rows - 1] = ss_cstr (ss); - } - - if (rows > MAX_PREVIEW_LINES) - { - case_unref (c); - break; - } - } - casereader_destroy (reader); - ia->line_cnt = rows; - } - else - { - GtkWidget * dialog = gtk_message_dialog_new (NULL, - GTK_DIALOG_MODAL, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - _("An error occurred reading the spreadsheet file.")); - - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); - } - } - - psppire_dict = psppire_dict_new_from_dict (ia->dict); - g_signal_connect (psppire_dict, "variable-changed", - G_CALLBACK (on_variable_change), ia); - ia->psppire_dict = psppire_dict; - - - /* XXX: PsppireVarStore doesn't hold a reference to - psppire_dict for now, but it should. After it does, we - should g_object_ref the psppire_dict here, since we also - hold a reference via ia->formats->dict. */ - var_sheet = PSPPIRE_VAR_SHEET (psppire_var_sheet_new ()); - g_object_set (var_sheet, - "dictionary", psppire_dict, - "may-create-vars", FALSE, - "may-delete-vars", FALSE, - "format-use", FMT_FOR_INPUT, - "enable-grid-lines", PSPP_SHEET_VIEW_GRID_LINES_BOTH, - (void *) NULL); - - vars_scroller = GTK_BIN (get_widget_assert (ia->builder, "vars-scroller")); - old_var_sheet = gtk_bin_get_child (GTK_BIN (vars_scroller)); - if (old_var_sheet != NULL) - gtk_container_remove (GTK_CONTAINER (vars_scroller), old_var_sheet); - gtk_container_add (GTK_CONTAINER (vars_scroller), GTK_WIDGET (var_sheet)); - gtk_widget_show (GTK_WIDGET (var_sheet)); - - ia->data_tree_view = - GTK_WIDGET (create_data_tree_view ( - FALSE, - GTK_CONTAINER (get_widget_assert (ia->builder, "data-scroller")), - ia)); - - gtk_widget_show (ia->paste_button); - - pop_watch_cursor (ia); } static void diff --git a/src/ui/gui/psppire-var-sheet-header.c b/src/ui/gui/psppire-var-sheet-header.c new file mode 100644 index 0000000000..cd2121fd8b --- /dev/null +++ b/src/ui/gui/psppire-var-sheet-header.c @@ -0,0 +1,130 @@ +/* + A candidate replacement for Pspp's sheet + Copyright (C) 2016 John Darrington + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include + +#include "psppire-var-sheet-header.h" + +#include "efficient-sheet/jmd-axis-model.h" +#include "efficient-sheet/jmd-datum.h" + +enum {CHANGED, + n_SIGNALS}; + +static guint signals [n_SIGNALS]; + +static guint +gni (GListModel *list) +{ + return 11; +} + +static GType +git (GListModel *list) +{ + return JMD_TYPE_DATUM; +} + +static gpointer +gi (GListModel *list, guint position) +{ + JmdDatum *gd = JMD_DATUM (g_object_new (JMD_TYPE_DATUM, NULL)); + + switch (position) + { + case 0: + gd->text = g_strdup ("Name"); + break; + case 1: + gd->text = g_strdup ("Type"); + break; + case 2: + gd->text = g_strdup ("Width"); + break; + case 3: + gd->text = g_strdup ("Decimal"); + break; + case 4: + gd->text = g_strdup ("Label"); + break; + case 5: + gd->text = g_strdup ("Value Labels"); + break; + case 6: + gd->text = g_strdup ("Missing Values"); + break; + case 7: + gd->text = g_strdup ("Columns"); + break; + case 8: + gd->text = g_strdup ("Align"); + break; + case 9: + gd->text = g_strdup ("Measure"); + break; + case 10: + gd->text = g_strdup ("Role"); + break; + default: + // g_assert_not_reached (); + g_print ("Bug: Request for item %d\n", position); + break; + } + + return gd; +} + + +static void +psppire_init_iface (GListModelInterface *iface) +{ + iface->get_n_items = gni; + iface->get_item = gi; + iface->get_item_type = git; +} + + +G_DEFINE_TYPE_WITH_CODE (PsppireVarSheetHeader, psppire_var_sheet_header, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, psppire_init_iface)); + +static void +psppire_var_sheet_header_init (PsppireVarSheetHeader *d) +{ +} + + + +static void +psppire_var_sheet_header_class_init (PsppireVarSheetHeaderClass *dc) +{ + GObjectClass *object_class = G_OBJECT_CLASS (dc); + + /* This signal is never emitted. It is just to satisfy the interface. */ + signals [CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + diff --git a/src/ui/gui/psppire-var-sheet-header.h b/src/ui/gui/psppire-var-sheet-header.h new file mode 100644 index 0000000000..476faaf0c8 --- /dev/null +++ b/src/ui/gui/psppire-var-sheet-header.h @@ -0,0 +1,40 @@ +/* + A candidate replacement for Pspp's sheet + Copyright (C) 2016 John Darrington + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _PSPPIRE_VAR_SHEET_HEADER_H +#define _PSPPIRE_VAR_SHEET_HEADER_H + + +G_DECLARE_FINAL_TYPE (PsppireVarSheetHeader, psppire_var_sheet_header, PSPPIRE, VAR_SHEET_HEADER, GObject) + + +struct _PsppireVarSheetHeader +{ + GObject parent_instance; +}; + +struct _PsppireVarSheetHeaderClass +{ + GObjectClass parent_instance; +}; + + + +#define PSPPIRE_TYPE_VAR_SHEET_HEADER psppire_var_sheet_header_get_type () + +#endif diff --git a/src/ui/gui/psppire-var-sheet.c b/src/ui/gui/psppire-var-sheet.c deleted file mode 100644 index 66a95deeed..0000000000 --- a/src/ui/gui/psppire-var-sheet.c +++ /dev/null @@ -1,1654 +0,0 @@ -/* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2008, 2009, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#include - -#include "ui/gui/psppire-var-sheet.h" - -#include "data/format.h" -#include "data/value-labels.h" -#include "libpspp/range-set.h" -#include "ui/gui/builder-wrapper.h" -#include "ui/gui/helper.h" -#include "ui/gui/missing-val-dialog.h" -#include "ui/gui/pspp-sheet-selection.h" -#include "ui/gui/psppire-cell-renderer-button.h" -#include "ui/gui/psppire-data-editor.h" -#include "ui/gui/psppire-data-window.h" -#include "ui/gui/psppire-dialog-action-var-info.h" -#include "ui/gui/psppire-dictview.h" -#include "ui/gui/psppire-empty-list-store.h" -#include "ui/gui/psppire-marshal.h" -#include "ui/gui/val-labs-dialog.h" -#include "ui/gui/var-type-dialog.h" -#include "ui/gui/var-display.h" -#include "ui/gui/var-type-dialog.h" - -#include "gl/intprops.h" - -#include -#define _(msgid) gettext (msgid) -#define N_(msgid) msgid - -enum vs_column - { - VS_NAME, - VS_TYPE, - VS_WIDTH, - VS_DECIMALS, - VS_LABEL, - VS_VALUES, - VS_MISSING, - VS_COLUMNS, - VS_ALIGN, - VS_MEASURE, - VS_ROLE - }; - -G_DEFINE_TYPE (PsppireVarSheet, psppire_var_sheet, PSPP_TYPE_SHEET_VIEW); - -static void -set_spin_cell (GtkCellRenderer *cell, int value, int min, int max, int step) -{ - char text[INT_BUFSIZE_BOUND (int)]; - GtkAdjustment *adjust; - - if (max > min) - adjust = GTK_ADJUSTMENT (gtk_adjustment_new (value, min, max, - step, step, 0.0)); - else - adjust = NULL; - - sprintf (text, "%d", value); - g_object_set (cell, - "text", text, - "adjustment", adjust, - "editable", TRUE, - NULL); -} - -static void -error_dialog (GtkWindow *w, gchar *primary_text, gchar *secondary_text) -{ - GtkWidget *dialog = - gtk_message_dialog_new (w, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, "%s", primary_text); - - g_object_set (dialog, "icon-name", "psppicon", NULL); - - gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), - "%s", secondary_text); - - gtk_dialog_run (GTK_DIALOG (dialog)); - - gtk_widget_destroy (dialog); -} - -static void -on_name_column_editing_started (GtkCellRenderer *cell, - GtkCellEditable *editable, - const gchar *path, - gpointer user_data) -{ - PsppireVarSheet *var_sheet = g_object_get_data (G_OBJECT (cell), "var-sheet"); - PsppireDict *dict = psppire_var_sheet_get_dictionary (var_sheet); - - g_return_if_fail (var_sheet); - g_return_if_fail (dict); - - if (GTK_IS_ENTRY (editable)) - { - GtkEntry *entry = GTK_ENTRY (editable); - if (gtk_entry_get_text (entry)[0] == '\0') - { - gchar name[64]; - if (psppire_dict_generate_name (dict, name, sizeof name)) - gtk_entry_set_text (entry, name); - } - } -} - -static void -scroll_to_bottom (GtkWidget *widget, - GtkRequisition *requisition, - gpointer unused UNUSED) -{ - PsppireVarSheet *var_sheet = PSPPIRE_VAR_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 (var_sheet->scroll_to_bottom_signal) - { - g_signal_handler_disconnect (var_sheet, - var_sheet->scroll_to_bottom_signal); - var_sheet->scroll_to_bottom_signal = 0; - } -} - -static struct variable * -path_string_to_var (PsppireVarSheet *var_sheet, gchar *path_string) -{ - GtkTreePath *path; - gint row; - - path = gtk_tree_path_new_from_string (path_string); - row = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - - return psppire_dict_get_variable (var_sheet->dict, row); -} - -static void -on_var_column_edited (GtkCellRendererText *cell, - gchar *path_string, - gchar *new_text, - gpointer user_data) -{ - PsppireVarSheet *var_sheet = user_data; - GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet))); - struct dictionary *dict = var_sheet->dict->dict; - enum vs_column column_id; - struct variable *var; - int width, decimals; - - column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), - "column-id")); - - var = path_string_to_var (var_sheet, path_string); - if (var == NULL) - { - g_return_if_fail (column_id == VS_NAME); - - if (!dict_id_is_valid (dict, new_text, false)) - error_dialog (window, - g_strdup (_("Cannot create variable.")), - g_strdup_printf (_("\"%s\" is not a valid variable " - "name."), new_text)); - else if (dict_lookup_var (dict, new_text) != NULL) - error_dialog (window, - g_strdup (_("Cannot create variable.")), - g_strdup_printf (_("This dictionary already contains " - "a variable named \"%s\"."), - new_text)); - else - { - dict_create_var (var_sheet->dict->dict, new_text, 0); - if (!var_sheet->scroll_to_bottom_signal) - { - gtk_widget_queue_resize (GTK_WIDGET (var_sheet)); - var_sheet->scroll_to_bottom_signal = - g_signal_connect (var_sheet, "size-allocate", - G_CALLBACK (scroll_to_bottom), NULL); - } - } - - return; - } - - switch (column_id) - { - case VS_NAME: - if (!dict_id_is_valid (dict, new_text, false)) - error_dialog (window, - g_strdup (_("Cannot rename variable.")), - g_strdup_printf (_("\"%s\" is not a valid variable " - "name."), new_text)); - else if (dict_lookup_var (dict, new_text) != NULL - && dict_lookup_var (dict, new_text) != var) - error_dialog (window, - g_strdup (_("Cannot rename variable.")), - g_strdup_printf (_("This dictionary already contains " - "a variable named \"%s\"."), - new_text)); - else - dict_rename_var (dict, var, new_text); - break; - - case VS_TYPE: - /* Not reachable. */ - break; - - case VS_WIDTH: - width = atoi (new_text); - if (width > 0) - { - struct fmt_spec format; - - format = *var_get_print_format (var); - fmt_change_width (&format, width, var_sheet->format_use); - var_set_width (var, fmt_var_width (&format)); - var_set_both_formats (var, &format); - } - break; - - case VS_DECIMALS: - decimals = atoi (new_text); - if (decimals >= 0) - { - struct fmt_spec format; - - format = *var_get_print_format (var); - fmt_change_decimals (&format, decimals, var_sheet->format_use); - var_set_print_format (var, &format); - } - break; - - case VS_LABEL: - var_set_label (var, new_text); - break; - - case VS_VALUES: - case VS_MISSING: - break; - - case VS_COLUMNS: - width = atoi (new_text); - if (width > 0 && width < 2 * MAX_STRING) - var_set_display_width (var, width); - break; - - case VS_ALIGN: - if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT))) - var_set_alignment (var, ALIGN_LEFT); - else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE))) - var_set_alignment (var, ALIGN_CENTRE); - else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT))) - var_set_alignment (var, ALIGN_RIGHT); - break; - - case VS_MEASURE: - if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL))) - var_set_measure (var, MEASURE_NOMINAL); - else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL))) - var_set_measure (var, MEASURE_ORDINAL); - else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE))) - var_set_measure (var, MEASURE_SCALE); - break; - - case VS_ROLE: - if (!strcmp (new_text, var_role_to_string (ROLE_INPUT))) - var_set_role (var, ROLE_INPUT); - else if (!strcmp (new_text, var_role_to_string (ROLE_TARGET))) - var_set_role (var, ROLE_TARGET); - else if (!strcmp (new_text, var_role_to_string (ROLE_BOTH))) - var_set_role (var, ROLE_BOTH); - else if (!strcmp (new_text, var_role_to_string (ROLE_NONE))) - var_set_role (var, ROLE_NONE); - else if (!strcmp (new_text, var_role_to_string (ROLE_PARTITION))) - var_set_role (var, ROLE_PARTITION); - else if (!strcmp (new_text, var_role_to_string (ROLE_SPLIT))) - var_set_role (var, ROLE_SPLIT); - break; - } -} - -static void -render_popup_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - void *user_data) -{ - PsppireVarSheet *var_sheet = user_data; - gint row; - - row = GPOINTER_TO_INT (iter->user_data); - g_object_set (cell, - "editable", row < psppire_dict_get_var_cnt (var_sheet->dict), - NULL); -} - -const char * -get_var_align_stock_id (enum alignment alignment) -{ - switch (alignment) - { - case ALIGN_LEFT: return "align-left"; - case ALIGN_CENTRE: return "align-center"; - case ALIGN_RIGHT: return "align-right"; - default: - g_return_val_if_reached (""); - } -} - -const char * -get_var_role_stock_id (enum var_role role) -{ - switch (role) - { - case ROLE_INPUT: return "role-input"; - case ROLE_TARGET: return "role-target"; - case ROLE_BOTH: return "role-both"; - case ROLE_NONE: return "role-none"; - case ROLE_PARTITION: return "role-partition"; - case ROLE_SPLIT: return "role-split"; - default: - g_return_val_if_reached (""); - } -} - -static void -render_var_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - void *user_data) -{ - PsppireVarSheet *var_sheet = user_data; - const struct fmt_spec *print; - enum vs_column column_id; - struct variable *var; - gint row; - - column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column), - "column-number")) - 1; - row = GPOINTER_TO_INT (iter->user_data); - - gtk_cell_renderer_set_visible (cell, true); - if (row >= psppire_dict_get_var_cnt (var_sheet->dict)) - { - if (GTK_IS_CELL_RENDERER_TEXT (cell)) - { - g_object_set (cell, - "text", "", - "editable", column_id == VS_NAME, - NULL); - if (column_id == VS_WIDTH - || column_id == VS_DECIMALS - || column_id == VS_COLUMNS) - g_object_set (cell, "adjustment", NULL, NULL); - } - else - { - gtk_cell_renderer_set_visible (cell, false); - } - return; - } - - var = psppire_dict_get_variable (var_sheet->dict, row); - - print = var_get_print_format (var); - switch (column_id) - { - case VS_NAME: - g_object_set (cell, - "text", var_get_name (var), - "editable", TRUE, - NULL); - break; - - case VS_TYPE: - g_object_set (cell, - "text", fmt_gui_name (print->type), - "editable", FALSE, - NULL); - break; - - case VS_WIDTH: - set_spin_cell (cell, print->w, - fmt_min_width (print->type, var_sheet->format_use), - fmt_max_width (print->type, var_sheet->format_use), - fmt_step_width (print->type)); - break; - - case VS_DECIMALS: - if (fmt_takes_decimals (print->type)) - { - int max_w = fmt_max_width (print->type, var_sheet->format_use); - int max_d = fmt_max_decimals (print->type, max_w, - var_sheet->format_use); - set_spin_cell (cell, print->d, 0, max_d, 1); - } - else - g_object_set (cell, - "text", "", - "editable", FALSE, - "adjustment", NULL, - NULL); - break; - - case VS_LABEL: - g_object_set (cell, - "text", var_has_label (var) ? var_get_label (var) : "", - "editable", TRUE, - NULL); - break; - - case VS_VALUES: - g_object_set (cell, "editable", FALSE, NULL); - if ( ! var_has_value_labels (var)) - g_object_set (cell, "text", _("None"), NULL); - else - { - const struct val_labs *vls = var_get_value_labels (var); - const struct val_lab **labels = val_labs_sorted (vls); - const struct val_lab *vl = labels[0]; - gchar *vstr = value_to_text (vl->value, var); - char *text = xasprintf (_("{%s, %s}..."), vstr, - val_lab_get_escaped_label (vl)); - free (vstr); - - g_object_set (cell, "text", text, NULL); - free (text); - free (labels); - } - break; - - case VS_MISSING: - { - char *text = missing_values_to_string (var, NULL); - g_object_set (cell, - "text", text, - "editable", FALSE, - NULL); - free (text); - } - break; - - case VS_COLUMNS: - set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1); - break; - - case VS_ALIGN: - if (GTK_IS_CELL_RENDERER_TEXT (cell)) - g_object_set (cell, - "text", alignment_to_string (var_get_alignment (var)), - "editable", TRUE, - NULL); - else - g_object_set (cell, "stock-id", - get_var_align_stock_id (var_get_alignment (var)), NULL); - break; - - case VS_MEASURE: - if (GTK_IS_CELL_RENDERER_TEXT (cell)) - g_object_set (cell, - "text", measure_to_string (var_get_measure (var)), - "editable", TRUE, - NULL); - else - { - enum fmt_type type = var_get_print_format (var)->type; - enum measure measure = var_get_measure (var); - - g_object_set (cell, "stock-id", - get_var_measurement_stock_id (type, measure), - NULL); - } - break; - - case VS_ROLE: - if (GTK_IS_CELL_RENDERER_TEXT (cell)) - g_object_set (cell, - "text", var_role_to_string (var_get_role (var)), - "editable", TRUE, - NULL); - else - g_object_set (cell, "stock-id", - get_var_role_stock_id (var_get_role (var)), NULL); - break; - } -} - -static struct variable * -path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string) -{ - PsppireDict *dict; - GtkTreePath *path; - gint row; - - path = gtk_tree_path_new_from_string (path_string); - row = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); - - dict = psppire_var_sheet_get_dictionary (var_sheet); - g_return_val_if_fail (dict != NULL, NULL); - - return psppire_dict_get_variable (dict, row); -} - -static void -on_type_click (PsppireCellRendererButton *cell, - gchar *path, - PsppireVarSheet *var_sheet) -{ - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)); - struct fmt_spec format; - struct variable *var; - - var = path_string_to_variable (var_sheet, path); - g_return_if_fail (var != NULL); - - format = *var_get_print_format (var); - psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format); - - var_set_width_and_formats (var, fmt_var_width (&format), &format, &format); -} - -static void -on_value_labels_click (PsppireCellRendererButton *cell, - gchar *path, - PsppireVarSheet *var_sheet) -{ - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)); - struct val_labs *labels; - struct variable *var; - - var = path_string_to_variable (var_sheet, path); - g_return_if_fail (var != NULL); - - labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var); - if (labels) - { - var_set_value_labels (var, labels); - val_labs_destroy (labels); - } -} - -static void -on_missing_values_click (PsppireCellRendererButton *cell, - gchar *path, - PsppireVarSheet *var_sheet) -{ - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)); - struct missing_values mv; - struct variable *var; - - var = path_string_to_variable (var_sheet, path); - g_return_if_fail (var != NULL); - - psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv); - var_set_missing_values (var, &mv); - mv_destroy (&mv); -} - -static gint -get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer, - const char *string) -{ - gint width; - g_object_set (G_OBJECT (renderer), - PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "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 PsppSheetViewColumn * -add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer, - enum vs_column column_id, - const char *title, int width) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - int title_width, content_width; - PsppSheetViewColumn *column; - - column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL); - g_object_set_data (G_OBJECT (column), "column-number", - GINT_TO_POINTER (column_id) + 1); - - pspp_sheet_view_column_set_cell_data_func ( - column, renderer, render_var_cell, var_sheet, NULL); - - title_width = get_string_width (sheet_view, renderer, title); - content_width = get_monospace_width (sheet_view, renderer, width); - g_object_set_data (G_OBJECT (column), "content-width", - GINT_TO_POINTER (content_width)); - - pspp_sheet_view_column_set_fixed_width (column, - MAX (title_width, content_width)); - pspp_sheet_view_column_set_resizable (column, TRUE); - - pspp_sheet_view_append_column (sheet_view, column); - - g_signal_connect (renderer, "edited", - G_CALLBACK (on_var_column_edited), - var_sheet); - g_object_set_data (G_OBJECT (renderer), "column-id", - GINT_TO_POINTER (column_id)); - g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet); - - return column; -} - -static PsppSheetViewColumn * -add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id, - const char *title, int width) -{ - return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (), - column_id, title, width); -} - -static PsppSheetViewColumn * -add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id, - const char *title, int width) -{ - return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (), - column_id, title, width); -} - -static const char * -measure_to_stock_id (enum fmt_type type, int measure) -{ - return get_var_measurement_stock_id (type, measure); -} - -static const char * -alignment_to_stock_id (enum fmt_type type, int alignment) -{ - return get_var_align_stock_id (alignment); -} - -static const char * -role_to_stock_id (enum fmt_type type, int role) -{ - return get_var_role_stock_id (role); -} - -static void -render_var_pixbuf (GtkCellLayout *cell_layout, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) -{ - const char *(*value_to_stock_id) (enum fmt_type, int value); - enum fmt_type type = GPOINTER_TO_INT (data); - gint value; - - value_to_stock_id = g_object_get_data (G_OBJECT (cell), "value-to-stock-id"); - - gtk_tree_model_get (tree_model, iter, 0, &value, -1); - g_object_set (cell, "stock-id", value_to_stock_id (type, value), NULL); -} - -static void -on_combo_editing_started (GtkCellRenderer *renderer, - GtkCellEditable *editable, - gchar *path_string, - gpointer user_data) -{ - PsppireVarSheet *var_sheet = user_data; - - if (GTK_IS_COMBO_BOX (editable)) - { - struct variable *var = path_string_to_variable (var_sheet, path_string); - const struct fmt_spec *format = var_get_print_format (var); - const char *(*value_to_stock_id) (enum fmt_type, int value); - GtkCellRenderer *cell; - - value_to_stock_id = g_object_get_data (G_OBJECT (renderer), - "value-to-stock-id"); - - cell = gtk_cell_renderer_pixbuf_new (); - g_object_set (cell, "width", 16, "height", 16, NULL); - g_object_set_data (G_OBJECT (cell), - "value-to-stock-id", value_to_stock_id); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editable), cell, FALSE); - gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (editable), cell, - render_var_pixbuf, - GINT_TO_POINTER (format->type), - NULL); - } -} - -static void -add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id, - const char *title, int width, - const char *(*value_to_stock_id) (enum fmt_type, int value), - ...) -{ - PsppSheetViewColumn *column; - GtkCellRenderer *cell; - GtkListStore *store; - const char *name; - va_list args; - - store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING); - va_start (args, value_to_stock_id); - while ((name = va_arg (args, const char *)) != NULL) - { - int value = va_arg (args, int); - gtk_list_store_insert_with_values (store, NULL, G_MAXINT, - 0, value, - 1, name, - -1); - } - va_end (args); - - cell = gtk_cell_renderer_combo_new (); - g_object_set (cell, - "has-entry", FALSE, - "model", GTK_TREE_MODEL (store), - "text-column", 1, - NULL); - if (value_to_stock_id != NULL) - { - g_object_set_data (G_OBJECT (cell), - "value-to-stock-id", value_to_stock_id); - g_signal_connect (cell, "editing-started", - G_CALLBACK (on_combo_editing_started), - var_sheet); - } - - column = add_var_sheet_column (var_sheet, cell, column_id, title, width); - - cell = gtk_cell_renderer_pixbuf_new (); - g_object_set (cell, "width", 16, "height", 16, NULL); - pspp_sheet_view_column_pack_end (column, cell, FALSE); - pspp_sheet_view_column_set_cell_data_func ( - column, cell, render_var_cell, var_sheet, NULL); -} - -static void -add_popup_menu (PsppireVarSheet *var_sheet, - PsppSheetViewColumn *column, - void (*on_click) (PsppireCellRendererButton *, - gchar *path, - PsppireVarSheet *var_sheet)) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - const char *button_label = "..."; - GtkCellRenderer *button_renderer; - gint content_width; - - button_renderer = psppire_cell_renderer_button_new (); - g_object_set (button_renderer, - "label", button_label, - "editable", TRUE, - NULL); - g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click), - var_sheet); - pspp_sheet_view_column_pack_start (column, button_renderer, FALSE); - pspp_sheet_view_column_set_cell_data_func ( - column, button_renderer, render_popup_cell, var_sheet, NULL); - - content_width = GPOINTER_TO_INT (g_object_get_data ( - G_OBJECT (column), "content-width")); - content_width += get_string_width (sheet_view, button_renderer, - button_label); - if (content_width > pspp_sheet_view_column_get_fixed_width (column)) - pspp_sheet_view_column_set_fixed_width (column, content_width); -} - -static gboolean -get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip, - gint wx, gint wy, size_t *row, size_t *column) -{ - PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); - gint bx, by; - GtkTreePath *path; - GtkTreeIter iter; - PsppSheetViewColumn *tree_column; - GtkTreeModel *tree_model; - gpointer column_ptr; - 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; - - column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number"); - if (column_ptr == NULL) - return FALSE; - *column = GPOINTER_TO_INT (column_ptr) - 1; - - 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_var_tooltip (GtkWidget *widget, gint wx, gint wy, - gboolean keyboard_mode UNUSED, - GtkTooltip *tooltip, gpointer *user_data UNUSED) -{ - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget); - PsppireDict *dict; - struct variable *var; - size_t row, column; - - if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column)) - return FALSE; - - dict = psppire_var_sheet_get_dictionary (var_sheet); - g_return_val_if_fail (dict != NULL, FALSE); - - if (row >= psppire_dict_get_var_cnt (dict)) - { - gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a " - "new variable.")); - return TRUE; - } - - var = psppire_dict_get_variable (dict, row); - g_return_val_if_fail (var != NULL, FALSE); - - switch (column) - { - case VS_TYPE: - { - char text[FMT_STRING_LEN_MAX + 1]; - - fmt_to_string (var_get_print_format (var), text); - gtk_tooltip_set_text (tooltip, text); - return TRUE; - } - - case VS_VALUES: - if (var_has_value_labels (var)) - { - const struct val_labs *vls = var_get_value_labels (var); - const struct val_lab **labels = val_labs_sorted (vls); - struct string s; - size_t i; - - ds_init_empty (&s); - for (i = 0; i < val_labs_count (vls); i++) - { - const struct val_lab *vl = labels[i]; - gchar *vstr; - - if (i >= 10 || ds_length (&s) > 500) - { - ds_put_cstr (&s, "..."); - break; - } - - vstr = value_to_text (vl->value, var); - ds_put_format (&s, _("{%s, %s}\n"), vstr, - val_lab_get_escaped_label (vl)); - free (vstr); - - } - ds_chomp_byte (&s, '\n'); - - gtk_tooltip_set_text (tooltip, ds_cstr (&s)); - ds_destroy (&s); - free (labels); - - return TRUE; - } - } - - return FALSE; -} - -static void -do_popup_menu (GtkWidget *widget, guint button, guint32 time) -{ - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget); - GtkWidget *menu; - - menu = get_widget_assert (var_sheet->builder, "varsheet-variable-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); - gtk_tree_path_free (path); - } - } - - do_popup_menu (widget, event->button, event->time); - return TRUE; - } - - return FALSE; -} - -GType -psppire_fmt_use_get_type (void) -{ - static GType etype = 0; - if (etype == 0) - { - static const GEnumValue values[] = - { - { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" }, - { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" }, - { 0, NULL, NULL } - }; - - etype = g_enum_register_static - (g_intern_static_string ("PsppireFmtUse"), values); - } - return etype; -} - -enum - { - PROP_0, - PROP_DICTIONARY, - PROP_MAY_CREATE_VARS, - PROP_MAY_DELETE_VARS, - PROP_FORMAT_TYPE, - PROP_UI_MANAGER - }; - -static void -psppire_var_sheet_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object); - - switch (prop_id) - { - case PROP_DICTIONARY: - psppire_var_sheet_set_dictionary (obj, - PSPPIRE_DICT (g_value_get_object ( - value))); - break; - - case PROP_MAY_CREATE_VARS: - psppire_var_sheet_set_may_create_vars (obj, - g_value_get_boolean (value)); - break; - - case PROP_MAY_DELETE_VARS: - psppire_var_sheet_set_may_delete_vars (obj, - g_value_get_boolean (value)); - break; - - case PROP_FORMAT_TYPE: - obj->format_use = g_value_get_enum (value); - break; - - case PROP_UI_MANAGER: - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_var_sheet_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object); - - switch (prop_id) - { - case PROP_DICTIONARY: - g_value_set_object (value, - G_OBJECT (psppire_var_sheet_get_dictionary (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_FORMAT_TYPE: - g_value_set_enum (value, obj->format_use); - break; - - case PROP_UI_MANAGER: - g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -psppire_var_sheet_dispose (GObject *obj) -{ - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj); - int i; - - if (var_sheet->dispose_has_run) - return; - - var_sheet->dispose_has_run = TRUE; - - for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++) - if ( var_sheet->dict_signals[i]) - g_signal_handler_disconnect (var_sheet->dict, - var_sheet->dict_signals[i]); - - if (var_sheet->dict) - g_object_unref (var_sheet->dict); - - if (var_sheet->uim) - g_object_unref (var_sheet->uim); - - /* These dialogs are not GObjects (although they should be!) - But for now, unreffing them only causes a GCritical Error - so comment them out for now. (and accept the memory leakage) - - g_object_unref (var_sheet->val_labs_dialog); - g_object_unref (var_sheet->missing_val_dialog); - g_object_unref (var_sheet->var_type_dialog); - */ - - g_object_unref (var_sheet->builder); - - - G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj); -} - -static void -psppire_var_sheet_class_init (PsppireVarSheetClass *class) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (class); - GParamSpec *pspec; - - gobject_class->set_property = psppire_var_sheet_set_property; - gobject_class->get_property = psppire_var_sheet_get_property; - gobject_class->dispose = psppire_var_sheet_dispose; - - 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); - - pspec = g_param_spec_object ("dictionary", - "Dictionary displayed by the sheet", - "The PsppireDict that the sheet displays " - "may allow the user to edit", - PSPPIRE_TYPE_DICT, - G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec); - - pspec = 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_CREATE_VARS, pspec); - - pspec = 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_MAY_DELETE_VARS, pspec); - - pspec = g_param_spec_enum ("format-use", - "Use of variable format", - ("Whether variables have input or output " - "formats"), - PSPPIRE_TYPE_FMT_USE, - FMT_FOR_OUTPUT, - G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec); - - pspec = g_param_spec_object ("ui-manager", - "UI Manager", - "UI manager for the variable sheet. The client should merge this UI manager with the active UI manager to obtain variable sheet specific menu items and tool bar items.", - GTK_TYPE_UI_MANAGER, - G_PARAM_READABLE); - g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec); -} - -static void -render_row_number_cell (PsppSheetViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) -{ - PsppireVarSheet *var_sheet = user_data; - GValue gvalue = { 0, }; - gint row; - - row = GPOINTER_TO_INT (iter->user_data); - - 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 (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict)) - g_object_set (cell, "editable", TRUE, NULL); - else - g_object_set (cell, "editable", FALSE, NULL); -} - -static void -psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button, - gchar *path_string, - PsppireVarSheet *var_sheet) -{ - GtkTreePath *path; - - g_return_if_fail (var_sheet->dict != NULL); - - path = gtk_tree_path_new_from_string (path_string); - if (gtk_tree_path_get_depth (path) == 1) - { - gint *indices = gtk_tree_path_get_indices (path); - if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict)) - { - gboolean handled; - g_signal_emit_by_name (var_sheet, "var-double-clicked", - indices[0], &handled); - } - } - gtk_tree_path_free (path); -} - -static void -psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column, - PsppireVarSheet *var_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - - pspp_sheet_selection_select_all (selection); -} - -static PsppSheetViewColumn * -make_row_number_column (PsppireVarSheet *var_sheet) -{ - PsppSheetViewColumn *column; - GtkCellRenderer *renderer; - - renderer = psppire_cell_renderer_button_new (); - g_object_set (renderer, "xalign", 1.0, NULL); - g_signal_connect (renderer, "double-clicked", - G_CALLBACK (psppire_var_sheet_row_number_double_clicked), - var_sheet); - - column = pspp_sheet_view_column_new_with_attributes (_("Variable"), - renderer, NULL); - pspp_sheet_view_column_set_clickable (column, TRUE); - pspp_sheet_view_column_set_cell_data_func ( - column, renderer, render_row_number_cell, var_sheet, NULL); - pspp_sheet_view_column_set_fixed_width (column, 50); - g_signal_connect (column, "clicked", - G_CALLBACK (psppire_var_sheet_variables_column_clicked), - var_sheet); - - return column; -} - -static void -on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDict *dict = var_sheet->dict; - 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)) - { - int i; - - for (i = 1; i <= range_set_node_get_width (node); i++) - { - unsigned long row = range_set_node_get_end (node) - i; - if (row < psppire_dict_get_var_cnt (dict)) - psppire_dict_delete_variables (dict, row, 1); - } - } - 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); - PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view); - gint n_selected_rows; - gboolean may_delete; - GtkTreePath *path; - GtkAction *action; - - n_selected_rows = pspp_sheet_selection_count_selected_rows (selection); - - action = get_action_assert (var_sheet->builder, "edit_insert-variable"); - gtk_action_set_sensitive (action, (var_sheet->may_create_vars - && n_selected_rows > 0)); - - switch (n_selected_rows) - { - case 0: - may_delete = FALSE; - break; - - case 1: - /* The row used for inserting new variables cannot be deleted. */ - path = gtk_tree_path_new_from_indices ( - psppire_dict_get_var_cnt (var_sheet->dict), -1); - may_delete = !pspp_sheet_selection_path_is_selected (selection, path); - gtk_tree_path_free (path); - break; - - default: - may_delete = TRUE; - break; - } - action = get_action_assert (var_sheet->builder, "edit_clear-variables"); - gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete); -} - -static void -on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDict *dict = var_sheet->dict; - 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_dict_get_var_cnt (dict)) - { - gchar name[64];; - if (psppire_dict_generate_name (dict, name, sizeof name)) - psppire_dict_insert_variable (dict, row, name); - } -} - -static void -psppire_var_sheet_init (PsppireVarSheet *obj) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj); - PsppSheetViewColumn *column; - GtkAction *action; - GList *list; - - obj->dict = NULL; - obj->format_use = FMT_FOR_OUTPUT; - obj->may_create_vars = TRUE; - obj->may_delete_vars = TRUE; - - obj->scroll_to_bottom_signal = 0; - - obj->container = NULL; - obj->dispose_has_run = FALSE; - obj->uim = NULL; - - pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj)); - - column = add_text_column (obj, VS_NAME, _("Name"), 12); - list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); - g_signal_connect (list->data, "editing-started", - G_CALLBACK (on_name_column_editing_started), NULL); - g_list_free (list); - - column = add_text_column (obj, VS_TYPE, _("Type"), 8); - add_popup_menu (obj, column, on_type_click); - - add_spin_column (obj, VS_WIDTH, _("Width"), 5); - - add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2); - - add_text_column (obj, VS_LABEL, _("Label"), 20); - - column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20); - add_popup_menu (obj, column, on_value_labels_click); - - column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20); - add_popup_menu (obj, column, on_missing_values_click); - - add_spin_column (obj, VS_COLUMNS, _("Columns"), 3); - - add_combo_column (obj, VS_ALIGN, _("Align"), 8, alignment_to_stock_id, - alignment_to_string (ALIGN_LEFT), ALIGN_LEFT, - alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE, - alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT, - NULL); - - add_combo_column (obj, VS_MEASURE, _("Measure"), 11, measure_to_stock_id, - measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL, - measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL, - measure_to_string (MEASURE_SCALE), MEASURE_SCALE, - NULL); - - add_combo_column (obj, VS_ROLE, _("Role"), 11, role_to_stock_id, - var_role_to_string (ROLE_INPUT), ROLE_INPUT, - var_role_to_string (ROLE_TARGET), ROLE_TARGET, - var_role_to_string (ROLE_BOTH), ROLE_BOTH, - var_role_to_string (ROLE_NONE), ROLE_NONE, - var_role_to_string (ROLE_PARTITION), ROLE_PARTITION, - var_role_to_string (ROLE_SPLIT), ROLE_SPLIT, - 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_MULTIPLE); - - g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL); - g_signal_connect (obj, "query-tooltip", - G_CALLBACK (on_query_var_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 ("var-sheet.ui"); - - action = get_action_assert (obj->builder, "edit_clear-variables"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables), - 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-variable"); - gtk_action_set_sensitive (action, FALSE); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable), - obj); -} - -GtkWidget * -psppire_var_sheet_new (void) -{ - return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL); -} - -PsppireDict * -psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet) -{ - return var_sheet->dict; -} - -static void -refresh_model (PsppireVarSheet *var_sheet) -{ - pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL); - - if (var_sheet->dict != NULL) - { - PsppireEmptyListStore *store; - int n_rows; - - n_rows = (psppire_dict_get_var_cnt (var_sheet->dict) - + var_sheet->may_create_vars); - store = psppire_empty_list_store_new (n_rows); - pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), - GTK_TREE_MODEL (store)); - g_object_unref (store); - } -} - -static void -on_var_changed (PsppireDict *dict, glong row, - guint what, const struct variable *oldvar, - PsppireVarSheet *var_sheet) -{ - PsppireEmptyListStore *store; - - g_return_if_fail (dict == var_sheet->dict); - - store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model ( - PSPP_SHEET_VIEW (var_sheet))); - g_return_if_fail (store != NULL); - - psppire_empty_list_store_row_changed (store, row); -} - -static void -on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet) -{ - PsppireEmptyListStore *store; - int n_rows; - - g_return_if_fail (dict == var_sheet->dict); - - store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model ( - PSPP_SHEET_VIEW (var_sheet))); - g_return_if_fail (store != NULL); - - n_rows = (psppire_dict_get_var_cnt (var_sheet->dict) - + var_sheet->may_create_vars); - psppire_empty_list_store_set_n_rows (store, n_rows); - psppire_empty_list_store_row_inserted (store, row); -} - -static void -on_var_deleted (PsppireDict *dict, - const struct variable *var, int dict_idx, int case_idx, - PsppireVarSheet *var_sheet) -{ - PsppireEmptyListStore *store; - int n_rows; - - g_return_if_fail (dict == var_sheet->dict); - - store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model ( - PSPP_SHEET_VIEW (var_sheet))); - g_return_if_fail (store != NULL); - - n_rows = (psppire_dict_get_var_cnt (var_sheet->dict) - + var_sheet->may_create_vars); - psppire_empty_list_store_set_n_rows (store, n_rows); - psppire_empty_list_store_row_deleted (store, dict_idx); -} - -static void -on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet) -{ - g_return_if_fail (dict == var_sheet->dict); - refresh_model (var_sheet); -} - -void -psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet, - PsppireDict *dict) -{ - if (var_sheet->dict != NULL) - { - int i; - - for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++) - { - if (var_sheet->dict_signals[i]) - g_signal_handler_disconnect (var_sheet->dict, - var_sheet->dict_signals[i]); - - var_sheet->dict_signals[i] = 0; - } - - g_object_unref (var_sheet->dict); - } - - var_sheet->dict = dict; - - if (dict != NULL) - { - g_object_ref (dict); - - var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED] - = g_signal_connect (dict, "backend-changed", - G_CALLBACK (on_backend_changed), var_sheet); - - var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED] - = g_signal_connect (dict, "variable-changed", - G_CALLBACK (on_var_changed), var_sheet); - - var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED] - = g_signal_connect (dict, "variable-inserted", - G_CALLBACK (on_var_inserted), var_sheet); - - var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED] - = g_signal_connect (dict, "variable-deleted", - G_CALLBACK (on_var_deleted), var_sheet); - } - - refresh_model (var_sheet); -} - -gboolean -psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet) -{ - return var_sheet->may_create_vars; -} - -void -psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet, - gboolean may_create_vars) -{ - if (var_sheet->may_create_vars != may_create_vars) - { - PsppireEmptyListStore *store; - gint n_rows; - - var_sheet->may_create_vars = may_create_vars; - - store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model ( - PSPP_SHEET_VIEW (var_sheet))); - g_return_if_fail (store != NULL); - - n_rows = (psppire_dict_get_var_cnt (var_sheet->dict) - + var_sheet->may_create_vars); - psppire_empty_list_store_set_n_rows (store, n_rows); - - if (may_create_vars) - psppire_empty_list_store_row_inserted (store, n_rows - 1); - else - psppire_empty_list_store_row_deleted (store, n_rows); - - on_selection_changed (pspp_sheet_view_get_selection ( - PSPP_SHEET_VIEW (var_sheet)), NULL); - } -} - -gboolean -psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet) -{ - return var_sheet->may_delete_vars; -} - -void -psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet, - gboolean may_delete_vars) -{ - if (var_sheet->may_delete_vars != may_delete_vars) - { - var_sheet->may_delete_vars = may_delete_vars; - on_selection_changed (pspp_sheet_view_get_selection ( - PSPP_SHEET_VIEW (var_sheet)), NULL); - } -} - -void -psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet); - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices (dict_index, -1); - pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0); - pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE); - gtk_tree_path_free (path); -} - -GtkUIManager * -psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet) -{ - if (var_sheet->uim == NULL) - { - var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder, - "var_sheet_uim", - GTK_TYPE_UI_MANAGER)); - g_object_ref (var_sheet->uim); - } - - return var_sheet->uim; -} - diff --git a/src/ui/gui/psppire-var-sheet.h b/src/ui/gui/psppire-var-sheet.h index e8db83e12a..3d10dd0d53 100644 --- a/src/ui/gui/psppire-var-sheet.h +++ b/src/ui/gui/psppire-var-sheet.h @@ -46,7 +46,6 @@ typedef struct _PsppireVarSheetClass PsppireVarSheetClass; enum { - PSPPIRE_VAR_SHEET_BACKEND_CHANGED, PSPPIRE_VAR_SHEET_VARIABLE_CHANGED, PSPPIRE_VAR_SHEET_VARIABLE_INSERTED, PSPPIRE_VAR_SHEET_VARIABLE_DELETED, diff --git a/src/ui/gui/widgets.c b/src/ui/gui/widgets.c index b9a7a1ec3e..3f138291ec 100644 --- a/src/ui/gui/widgets.c +++ b/src/ui/gui/widgets.c @@ -4,6 +4,7 @@ #include "widgets.h" +#include "gettext.h" #include "psppire-dialog.h" #include "psppire-selector.h" @@ -121,6 +122,62 @@ preregister_actions (void) } +static void +tx_string_to_double (const GValue *src, GValue *dest) +{ + const gchar *str = g_value_get_string (src); + gdouble dble = g_strtod (str, NULL); + g_value_set_double (dest, dble); +} + + +static void +tx_string_to_int (const GValue *src, GValue *dest) +{ + const gchar *str = g_value_get_string (src); + gint x = atoi (str); + g_value_set_int (dest, x); +} + +static void +enum_to_string (const GValue *src, GValue *dest) +{ + gint n = g_value_get_enum (src); + GType t = G_VALUE_TYPE (src); + GEnumClass *ec = g_type_class_ref (t); + GEnumValue *ev = g_enum_get_value (ec, n); + + g_value_set_string (dest, gettext (ev->value_nick)); +} + + + +GType align_enum_type; +GType measure_enum_type; +GType role_enum_type; + + +extern const GEnumValue align[]; +extern const GEnumValue measure[]; +extern const GEnumValue role[]; + + + +static void +preregister_misc (void) +{ + align_enum_type = g_enum_register_static ("PsppAlignment", align); + measure_enum_type = g_enum_register_static ("PsppMeasure", measure); + role_enum_type = g_enum_register_static ("PsppRole", role); + + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_DOUBLE, tx_string_to_double); + g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, tx_string_to_int); + + g_value_register_transform_func (measure_enum_type, G_TYPE_STRING, enum_to_string); + g_value_register_transform_func (align_enum_type, G_TYPE_STRING, enum_to_string); + g_value_register_transform_func (role_enum_type, G_TYPE_STRING, enum_to_string); +} + /* Any custom widgets which are to be used in GtkBuilder ui files need to be preregistered, otherwise GtkBuilder refuses to @@ -142,6 +199,7 @@ preregister_widgets (void) psppire_means_layer_get_type (); preregister_actions (); + preregister_misc (); /* This seems to be necessary on Cygwin. It ought not to be necessary. Having it here can't do any harm. */