[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[datasets 12/18] gui: Eliminate dataset-related global variables.
From: |
Ben Pfaff |
Subject: |
[datasets 12/18] gui: Eliminate dataset-related global variables. |
Date: |
Sat, 30 Apr 2011 22:36:40 -0700 |
This should make it easier to introduce support for multiple datasets
later.
---
src/ui/gui/executor.c | 25 +++----
src/ui/gui/psppire-data-editor.c | 32 +++++++-
src/ui/gui/psppire-data-editor.h | 3 +-
src/ui/gui/psppire-data-window.c | 149 ++++++++++++++++++++++++++++--------
src/ui/gui/psppire-data-window.h | 8 ++-
src/ui/gui/psppire-syntax-window.c | 26 +++----
src/ui/gui/psppire.c | 49 ++----------
src/ui/gui/psppire.h | 3 -
8 files changed, 185 insertions(+), 110 deletions(-)
diff --git a/src/ui/gui/executor.c b/src/ui/gui/executor.c
index 584b137..6a3f1e1 100644
--- a/src/ui/gui/executor.c
+++ b/src/ui/gui/executor.c
@@ -27,9 +27,6 @@
#include "ui/gui/psppire-data-store.h"
#include "ui/gui/psppire-output-window.h"
-extern struct dataset *the_dataset;
-extern PsppireDataStore *the_data_store;
-
/* Lazy casereader callback function used by execute_syntax. */
static struct casereader *
create_casereader_from_data_store (void *data_store_)
@@ -62,14 +59,14 @@ execute_syntax (PsppireDataWindow *window, struct
lex_reader *lex_reader)
needed. If the data store casereader is never needed, then
it is reused the next time syntax is run, without wrapping
it in another layer. */
- proto = psppire_data_store_get_proto (the_data_store);
- case_cnt = psppire_data_store_get_case_count (the_data_store);
+ proto = psppire_data_store_get_proto (window->data_store);
+ case_cnt = psppire_data_store_get_case_count (window->data_store);
reader = lazy_casereader_create (proto, case_cnt,
create_casereader_from_data_store,
- the_data_store, &lazy_serial);
- dataset_set_source (the_dataset, reader);
+ window->data_store, &lazy_serial);
+ dataset_set_source (window->dataset, reader);
- g_return_val_if_fail (dataset_has_source (the_dataset), FALSE);
+ g_return_val_if_fail (dataset_has_source (window->dataset), FALSE);
lexer = lex_create ();
psppire_set_lexer (lexer);
@@ -77,7 +74,7 @@ execute_syntax (PsppireDataWindow *window, struct lex_reader
*lex_reader)
for (;;)
{
- enum cmd_result result = cmd_parse (lexer, the_dataset);
+ enum cmd_result result = cmd_parse (lexer, window->dataset);
if ( cmd_result_is_failure (result))
{
@@ -90,14 +87,14 @@ execute_syntax (PsppireDataWindow *window, struct
lex_reader *lex_reader)
break;
}
- proc_execute (the_dataset);
+ proc_execute (window->dataset);
- psppire_dict_replace_dictionary (the_data_store->dict,
- dataset_dict (the_dataset));
+ psppire_dict_replace_dictionary (window->data_store->dict,
+ dataset_dict (window->dataset));
- reader = dataset_steal_source (the_dataset);
+ reader = dataset_steal_source (window->dataset);
if (!lazy_casereader_destroy (reader, lazy_serial))
- psppire_data_store_set_reader (the_data_store, reader);
+ psppire_data_store_set_reader (window->data_store, reader);
/* Destroy the lexer only after obtaining the dataset, because the dataset
might depend on the lexer, if the casereader specifies inline data. (In
diff --git a/src/ui/gui/psppire-data-editor.c b/src/ui/gui/psppire-data-editor.c
index 99bc907..c5a3cd3 100644
--- a/src/ui/gui/psppire-data-editor.c
+++ b/src/ui/gui/psppire-data-editor.c
@@ -89,6 +89,7 @@ psppire_data_editor_dispose (GObject *obj)
if (de->dispose_has_run)
return;
+ g_object_unref (de->data_window);
g_object_unref (de->data_store);
g_object_unref (de->var_store);
@@ -195,6 +196,7 @@ traverse_cell_callback (PsppireSheet *sheet,
enum
{
PROP_0,
+ PROP_DATA_WINDOW,
PROP_DATA_STORE,
PROP_VAR_STORE,
PROP_VS_ROW_MENU,
@@ -374,6 +376,10 @@ psppire_data_editor_set_property (GObject *object,
case PROP_SPLIT_WINDOW:
psppire_data_editor_split_window (de, g_value_get_boolean (value));
break;
+ case PROP_DATA_WINDOW:
+ de->data_window = g_value_get_pointer (value);
+ g_object_ref (de->data_window);
+ break;
case PROP_DATA_STORE:
if ( de->data_store) g_object_unref (de->data_store);
de->data_store = g_value_get_pointer (value);
@@ -501,6 +507,9 @@ psppire_data_editor_get_property (GObject *object,
case PROP_SPLIT_WINDOW:
g_value_set_boolean (value, de->split);
break;
+ case PROP_DATA_WINDOW:
+ g_value_set_pointer (value, de->data_window);
+ break;
case PROP_DATA_STORE:
g_value_set_pointer (value, de->data_store);
break;
@@ -534,6 +543,7 @@ psppire_data_editor_get_property (GObject *object,
static void
psppire_data_editor_class_init (PsppireDataEditorClass *klass)
{
+ GParamSpec *data_window_spec ;
GParamSpec *data_store_spec ;
GParamSpec *var_store_spec ;
GParamSpec *column_menu_spec;
@@ -556,6 +566,16 @@ psppire_data_editor_class_init (PsppireDataEditorClass
*klass)
+ data_window_spec =
+ g_param_spec_pointer ("data-window",
+ "Data Window",
+ "A pointer to the data window associated with this
editor",
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
G_PARAM_READABLE );
+
+ g_object_class_install_property (object_class,
+ PROP_DATA_WINDOW,
+ data_window_spec);
+
data_store_spec =
g_param_spec_pointer ("data-store",
"Data Store",
@@ -1008,13 +1028,15 @@ psppire_data_editor_init (PsppireDataEditor *de)
GtkWidget*
-psppire_data_editor_new (PsppireVarStore *var_store,
+psppire_data_editor_new (PsppireDataWindow *data_window,
+ PsppireVarStore *var_store,
PsppireDataStore *data_store)
{
return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
- "var-store", var_store,
- "data-store", data_store,
- NULL);
+ "data-window", data_window,
+ "var-store", var_store,
+ "data-store", data_store,
+ NULL);
}
@@ -1254,7 +1276,7 @@ do_sort (PsppireDataEditor *de, int var, gboolean descend)
syntax = g_strdup_printf ("SORT CASES BY %s%s.",
var_get_name (v), descend ? " (D)" : "");
- g_free (execute_syntax_string (psppire_default_data_window (), syntax));
+ g_free (execute_syntax_string (de->data_window, syntax));
}
diff --git a/src/ui/gui/psppire-data-editor.h b/src/ui/gui/psppire-data-editor.h
index 0c555a9..f6f55d7 100644
--- a/src/ui/gui/psppire-data-editor.h
+++ b/src/ui/gui/psppire-data-editor.h
@@ -48,6 +48,7 @@ struct _PsppireDataEditor
GtkWidget *cell_ref_entry;
GtkWidget *datum_entry;
GtkWidget *var_sheet;
+ struct _PsppireDataWindow *data_window;
PsppireDataStore *data_store;
PsppireVarStore *var_store;
@@ -74,7 +75,7 @@ struct _PsppireDataEditorClass
GType psppire_data_editor_get_type (void);
-GtkWidget* psppire_data_editor_new (PsppireVarStore *,
PsppireDataStore *);
+GtkWidget* psppire_data_editor_new (struct _PsppireDataWindow
*, PsppireVarStore *, PsppireDataStore *);
void psppire_data_editor_clip_copy (PsppireDataEditor *);
void psppire_data_editor_clip_paste (PsppireDataEditor *);
void psppire_data_editor_clip_cut (PsppireDataEditor *);
diff --git a/src/ui/gui/psppire-data-window.c b/src/ui/gui/psppire-data-window.c
index 74c4ca6..7e248f8 100644
--- a/src/ui/gui/psppire-data-window.c
+++ b/src/ui/gui/psppire-data-window.c
@@ -66,7 +66,7 @@
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid
-
+static PsppireDataWindow *the_data_window;
static void psppire_data_window_class_init (PsppireDataWindowClass *class);
static void psppire_data_window_init (PsppireDataWindow
*data_editor);
@@ -75,6 +75,14 @@ static void psppire_data_window_init
(PsppireDataWindow *data_edit
static void psppire_data_window_iface_init (PsppireWindowIface *iface);
static void psppire_data_window_dispose (GObject *object);
+static void psppire_data_window_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void psppire_data_window_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
GType
psppire_data_window_get_type (void)
@@ -118,6 +126,10 @@ psppire_data_window_get_type (void)
static GObjectClass *parent_class ;
+enum {
+ PROP_DATASET = 1
+};
+
static void
psppire_data_window_class_init (PsppireDataWindowClass *class)
{
@@ -126,14 +138,17 @@ psppire_data_window_class_init (PsppireDataWindowClass
*class)
parent_class = g_type_class_peek_parent (class);
object_class->dispose = psppire_data_window_dispose;
+ object_class->set_property = psppire_data_window_set_property;
+ object_class->get_property = psppire_data_window_get_property;
+
+ g_object_class_install_property (
+ object_class, PROP_DATASET,
+ g_param_spec_pointer ("dataset", "Dataset",
+ "'struct datset *' represented by the window",
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
}
-
-extern PsppireVarStore *the_var_store;
-extern struct dataset *the_dataset;
-extern PsppireDataStore *the_data_store ;
-
extern GtkRecentManager *the_recent_mgr;
static void
@@ -771,12 +786,12 @@ toggle_split_window (PsppireDataWindow *de,
GtkToggleAction *ta)
static void
-file_quit (void)
+file_quit (PsppireDataWindow *de)
{
/* FIXME: Need to be more intelligent here.
Give the user the opportunity to save any unsaved data.
*/
- g_object_unref (the_data_store);
+ g_object_unref (de->data_store);
psppire_quit ();
}
@@ -907,42 +922,68 @@ connect_action (PsppireDataWindow *dw, const char
*action_name,
return action;
}
+/* Initializes as much of a PsppireDataWindow as we can and must before the
+ dataset has been set.
+
+ In particular, the 'menu' member is required in case the "filename" property
+ is set before the "dataset" property: otherwise PsppireWindow will try to
+ modify the menu as part of the "filename" property_set() function and end up
+ with a Gtk-CRITICAL since 'menu' is NULL. */
static void
psppire_data_window_init (PsppireDataWindow *de)
{
+ GtkUIManager *uim;
+
+ de->builder = builder_new ("data-editor.ui");
+
+ uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1",
GTK_TYPE_UI_MANAGER));
+
+ PSPPIRE_WINDOW (de)->menu =
+ GTK_MENU_SHELL (gtk_ui_manager_get_widget
(uim,"/ui/menubar/windows/windows_minimise_all")->parent);
+}
+
+static void
+psppire_data_window_finish_init (PsppireDataWindow *de,
+ struct dataset *ds)
+{
static const struct dataset_callbacks cbs =
{
set_unsaved, /* changed */
transformation_change_callback, /* transformations_changed */
};
- PsppireVarStore *vs;
- PsppireDict *dict = NULL;
+ PsppireDict *dict;
GtkWidget *menubar;
GtkWidget *hb ;
GtkWidget *sb ;
GtkWidget *box = gtk_vbox_new (FALSE, 0);
- de->builder = builder_new ("data-editor.ui");
+
+ de->dataset = ds;
+ dict = psppire_dict_new_from_dict (dataset_dict (ds));
+ de->var_store = psppire_var_store_new (dict);
+ de->data_store = psppire_data_store_new (dict);
+ psppire_data_store_set_reader (de->data_store, NULL);
menubar = get_widget_assert (de->builder, "menubar");
hb = get_widget_assert (de->builder, "handlebox1");
sb = get_widget_assert (de->builder, "status-bar");
de->data_editor =
- PSPPIRE_DATA_EDITOR (psppire_data_editor_new (the_var_store,
the_data_store));
+ PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
+ de->data_store));
- g_signal_connect_swapped (the_data_store, "case-changed",
+ g_signal_connect_swapped (de->data_store, "case-changed",
G_CALLBACK (set_unsaved), de);
- g_signal_connect_swapped (the_data_store, "case-inserted",
+ g_signal_connect_swapped (de->data_store, "case-inserted",
G_CALLBACK (set_unsaved), de);
- g_signal_connect_swapped (the_data_store, "cases-deleted",
+ g_signal_connect_swapped (de->data_store, "cases-deleted",
G_CALLBACK (set_unsaved), de);
- dataset_set_callbacks (the_dataset, &cbs, de);
+ dataset_set_callbacks (de->dataset, &cbs, de);
connect_help (de->builder);
@@ -964,12 +1005,6 @@ psppire_data_window_init (PsppireDataWindow *de)
g_signal_connect_swapped (de->data_editor, "data-available-changed",
G_CALLBACK (set_paste_menuitem_sensitivity), de);
- vs = the_var_store;
-
- g_assert(vs); /* Traps a possible bug in w32 build */
-
- g_object_get (vs, "dictionary", &dict, NULL);
-
g_signal_connect (dict, "weight-changed",
G_CALLBACK (on_weight_change),
de);
@@ -1183,9 +1218,6 @@ psppire_data_window_init (PsppireDataWindow *de)
GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder,
"uimanager1", GTK_TYPE_UI_MANAGER));
merge_help_menu (uim);
-
- PSPPIRE_WINDOW (de)->menu =
- GTK_MENU_SHELL (gtk_ui_manager_get_widget
(uim,"/ui/menubar/windows/windows_minimise_all")->parent);
}
{
@@ -1215,6 +1247,8 @@ psppire_data_window_init (PsppireDataWindow *de)
gtk_widget_show (GTK_WIDGET (de->data_editor));
gtk_widget_show (box);
+
+ the_data_window = de;
}
static void
@@ -1228,17 +1262,62 @@ psppire_data_window_dispose (GObject *object)
dw->builder = NULL;
}
+ if (the_data_window == dw)
+ the_data_window = NULL;
+
G_OBJECT_CLASS (parent_class)->dispose (object);
}
+static void
+psppire_data_window_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
+
+ switch (prop_id)
+ {
+ case PROP_DATASET:
+ psppire_data_window_finish_init (window, g_value_get_pointer (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ };
+}
+
+static void
+psppire_data_window_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
+
+ switch (prop_id)
+ {
+ case PROP_DATASET:
+ g_value_set_pointer (value, window->dataset);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ };
+}
+
+
GtkWidget*
-psppire_data_window_new (void)
+psppire_data_window_new (struct dataset *ds)
{
- return GTK_WIDGET (g_object_new (psppire_data_window_get_type (),
- /* TRANSLATORS: This will form a filename.
Please avoid whitespace. */
- "filename", _("PSPP-data"),
- "description", _("Data Editor"),
- NULL));
+ return GTK_WIDGET (
+ g_object_new (
+ psppire_data_window_get_type (),
+ /* TRANSLATORS: This will form a filename. Please avoid whitespace. */
+ "filename", _("PSPP-data"),
+ "description", _("Data Editor"),
+ "dataset", ds,
+ NULL));
}
@@ -1249,3 +1328,11 @@ psppire_data_window_iface_init (PsppireWindowIface
*iface)
iface->load = load_file;
}
+
+PsppireDataWindow *
+psppire_default_data_window (void)
+{
+ if (the_data_window == NULL)
+ gtk_widget_show (psppire_data_window_new (dataset_create ()));
+ return the_data_window;
+}
diff --git a/src/ui/gui/psppire-data-window.h b/src/ui/gui/psppire-data-window.h
index 1e02493..0100254 100644
--- a/src/ui/gui/psppire-data-window.h
+++ b/src/ui/gui/psppire-data-window.h
@@ -26,6 +26,8 @@
#include "ui/gui/psppire-window.h"
#include "ui/gui/psppire-data-editor.h"
+struct dataset;
+
G_BEGIN_DECLS
#define PSPPIRE_DATA_WINDOW_TYPE (psppire_data_window_get_type ())
@@ -50,6 +52,9 @@ struct _PsppireDataWindow
PsppireDataEditor *data_editor;
GtkBuilder *builder;
+ PsppireVarStore *var_store;
+ struct dataset *dataset;
+ PsppireDataStore *data_store;
GtkAction *invoke_goto_dialog;
@@ -68,7 +73,8 @@ struct _PsppireDataWindowClass
};
GType psppire_data_window_get_type (void);
-GtkWidget* psppire_data_window_new (void);
+GtkWidget* psppire_data_window_new (struct dataset *);
+PsppireDataWindow *psppire_default_data_window (void);
G_END_DECLS
diff --git a/src/ui/gui/psppire-syntax-window.c
b/src/ui/gui/psppire-syntax-window.c
index 6629235..45b6521 100644
--- a/src/ui/gui/psppire-syntax-window.c
+++ b/src/ui/gui/psppire-syntax-window.c
@@ -17,20 +17,21 @@
#include <config.h>
#include <gtk/gtk.h>
-#include "executor.h"
-#include "helper.h"
-
-#include <language/lexer/lexer.h>
-#include <libpspp/message.h>
#include <stdlib.h>
-#include "help-menu.h"
-#include "psppire.h"
-#include "psppire-data-window.h"
-#include "psppire-window-register.h"
-#include "psppire-syntax-window.h"
+#include "language/lexer/lexer.h"
+#include "libpspp/message.h"
+#include "ui/gui/executor.h"
+#include "ui/gui/help-menu.h"
+#include "ui/gui/helper.h"
+#include "ui/gui/psppire-data-window.h"
+#include "ui/gui/psppire-syntax-window.h"
+#include "ui/gui/psppire-syntax-window.h"
+#include "ui/gui/psppire-window-register.h"
+#include "ui/gui/psppire.h"
+#include "ui/gui/psppire.h"
-#include "xalloc.h"
+#include "gl/xalloc.h"
#include <gettext.h>
#define _(msgid) gettext (msgid)
@@ -165,10 +166,7 @@ editor_execute_syntax (const PsppireSyntaxWindow *sw,
GtkTextIter start,
execute_syntax (psppire_default_data_window (), reader);
}
-
-
-
/* Delete the currently selected text */
static void
on_edit_delete (PsppireSyntaxWindow *sw)
diff --git a/src/ui/gui/psppire.c b/src/ui/gui/psppire.c
index 1b11c11..80983e0 100644
--- a/src/ui/gui/psppire.c
+++ b/src/ui/gui/psppire.c
@@ -59,34 +59,19 @@
#include "gl/xalloc.h"
#include "gl/relocatable.h"
-GtkRecentManager *the_recent_mgr = 0;
-PsppireDataStore *the_data_store = 0;
-PsppireVarStore *the_var_store = 0;
+GtkRecentManager *the_recent_mgr;
static void create_icon_factory (void);
-
-struct dataset * the_dataset = NULL;
-
-static GtkWidget *the_data_window;
-
-static void load_data_file (const char *);
-
-static void
-replace_casereader (struct casereader *s)
-{
- psppire_data_store_set_reader (the_data_store, s);
-}
+static void load_data_file (PsppireDataWindow *, const char *);
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid
-
-
void
initialize (const char *data_file)
{
- PsppireDict *dictionary = 0;
+ PsppireDataWindow *data_window;
i18n_init ();
@@ -96,19 +81,10 @@ initialize (const char *data_file)
settings_init ();
fh_init ();
- the_dataset = dataset_create ();
psppire_set_lexer (NULL);
- dictionary = psppire_dict_new_from_dict (dataset_dict (the_dataset));
-
bind_textdomain_codeset (PACKAGE, "UTF-8");
- /* Create the model for the var_sheet */
- the_var_store = psppire_var_store_new (dictionary);
-
- the_data_store = psppire_data_store_new (dictionary);
- replace_casereader (NULL);
-
create_icon_factory ();
psppire_output_window_setup ();
@@ -123,13 +99,10 @@ initialize (const char *data_file)
psppire_selector_set_default_selection_func (PSPPIRE_VAR_VIEW_TYPE,
insert_source_row_into_tree_view);
psppire_selector_set_default_selection_func (GTK_TYPE_TREE_VIEW,
insert_source_row_into_tree_view);
- the_data_window = psppire_data_window_new ();
+ data_window = psppire_default_data_window ();
if (data_file != NULL)
- load_data_file (data_file);
-
- execute_const_syntax_string (PSPPIRE_DATA_WINDOW (the_data_window), "");
-
- gtk_widget_show (the_data_window);
+ load_data_file (data_window, data_file);
+ execute_const_syntax_string (data_window, "");
}
@@ -141,12 +114,6 @@ de_initialize (void)
i18n_done ();
}
-PsppireDataWindow *
-psppire_default_data_window (void)
-{
- return PSPPIRE_DATA_WINDOW (the_data_window);
-}
-
static void
func (gpointer key, gpointer value, gpointer data)
{
@@ -239,7 +206,7 @@ create_icon_factory (void)
}
static void
-load_data_file (const char *arg)
+load_data_file (PsppireDataWindow *window, const char *arg)
{
gchar *filename = NULL;
gchar *utf8 = NULL;
@@ -291,7 +258,7 @@ load_data_file (const char *arg)
if ( filename == NULL)
filename = xstrdup (arg);
- psppire_window_load (PSPPIRE_WINDOW (the_data_window), filename);
+ psppire_window_load (PSPPIRE_WINDOW (window), filename);
g_free (filename);
}
diff --git a/src/ui/gui/psppire.h b/src/ui/gui/psppire.h
index 4b9cdbe..8817824 100644
--- a/src/ui/gui/psppire.h
+++ b/src/ui/gui/psppire.h
@@ -17,8 +17,6 @@
#ifndef PSPPIRE_H
#define PSPPIRE_H
-#include "ui/gui/psppire-data-window.h"
-
struct lexer;
void initialize (const char *data_file);
@@ -29,6 +27,5 @@ void psppire_quit (void);
const char * output_file_name (void);
void psppire_set_lexer (struct lexer *);
-PsppireDataWindow *psppire_default_data_window (void);
#endif /* PSPPIRE_H */
--
1.7.2.5
- [datasets 16/18] Implement DATASET commands., (continued)
- [datasets 16/18] Implement DATASET commands., Ben Pfaff, 2011/05/01
- [datasets 10/18] gui: Prefer NULL to 0 for initializing pointers., Ben Pfaff, 2011/05/01
- [datasets 18/18] gui: Change View|Data and View|Variables to radio button menu items., Ben Pfaff, 2011/05/01
- [datasets 15/18] gui: New "entry-dialog" module for prompting for a text string., Ben Pfaff, 2011/05/01
- [datasets 12/18] gui: Eliminate dataset-related global variables.,
Ben Pfaff <=
- [datasets 11/18] gui: Make syntax execution functions take a PsppireDataWindow argument., Ben Pfaff, 2011/05/01
- [datasets 04/18] gui: Fix const-ness warning in create_lines_tree_view()., Ben Pfaff, 2011/05/01
- [datasets 05/18] gui: Drop null base_finalize function from PsppireDataWindow., Ben Pfaff, 2011/05/01