1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 #include <gtk/gtksignal.h>
20 #include <gtksheet/gtkextra-sheet.h>
21 #include "psppire-data-editor.h"
22 #include "psppire-var-sheet.h"
24 #include <language/syntax-string-source.h>
25 #include "psppire-data-store.h"
26 #include <gtksheet/psppire-axis-impl.h>
29 #include <gtksheet/gtkxpaned.h>
31 #define _(msgid) gettext (msgid)
32 #define N_(msgid) msgid
35 static void psppire_data_editor_remove_split (PsppireDataEditor *de);
36 static void psppire_data_editor_set_split (PsppireDataEditor *de);
39 DATA_SELECTION_CHANGED,
40 DATA_AVAILABLE_CHANGED,
47 static guint data_editor_signals [n_SIGNALS] = { 0 };
50 static gboolean data_is_selected (PsppireDataEditor *de);
52 static void psppire_data_editor_class_init (PsppireDataEditorClass *klass);
53 static void psppire_data_editor_init (PsppireDataEditor *de);
56 psppire_data_editor_get_type (void)
58 static GType de_type = 0;
62 static const GTypeInfo de_info =
64 sizeof (PsppireDataEditorClass),
66 NULL, /* base_finalize */
67 (GClassInitFunc) psppire_data_editor_class_init,
68 NULL, /* class_finalize */
69 NULL, /* class_data */
70 sizeof (PsppireDataEditor),
72 (GInstanceInitFunc) psppire_data_editor_init,
75 de_type = g_type_register_static (GTK_TYPE_NOTEBOOK, "PsppireDataEditor",
82 static GObjectClass * parent_class = NULL;
85 psppire_data_editor_dispose (GObject *obj)
87 PsppireDataEditor *de = (PsppireDataEditor *) obj;
89 if (de->dispose_has_run)
92 g_object_unref (de->data_store);
93 g_object_unref (de->var_store);
95 /* Make sure dispose does not run twice. */
96 de->dispose_has_run = TRUE;
98 /* Chain up to the parent class */
99 G_OBJECT_CLASS (parent_class)->dispose (obj);
103 psppire_data_editor_finalize (GObject *obj)
105 /* Chain up to the parent class */
106 G_OBJECT_CLASS (parent_class)->finalize (obj);
111 static void popup_variable_column_menu (GtkSheet *sheet, gint column,
112 GdkEventButton *event, gpointer data);
114 static void popup_variable_row_menu (GtkSheet *sheet, gint row,
115 GdkEventButton *event, gpointer data);
118 static void popup_cases_menu (GtkSheet *sheet, gint row,
119 GdkEventButton *event, gpointer data);
125 /* Callback which occurs when the data sheet's column title
128 on_data_column_clicked (PsppireDataEditor *de, gint col, gpointer data)
130 GtkSheetRange visible_range;
131 gint current_row, current_column;
133 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
134 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
136 gtk_sheet_get_active_cell (GTK_SHEET (de->var_sheet),
137 ¤t_row, ¤t_column);
139 gtk_sheet_set_active_cell (GTK_SHEET (de->var_sheet), col, current_column);
142 gtk_sheet_get_visible_range (GTK_SHEET (de->var_sheet), &visible_range);
144 if ( col < visible_range.row0 || col > visible_range.rowi)
145 gtk_sheet_moveto (GTK_SHEET (de->var_sheet), col, current_column, 0.5, 0.5);
152 /* Callback which occurs when the var sheet's row title
153 button is double clicked */
155 on_var_row_clicked (PsppireDataEditor *de, gint row, gpointer data)
157 GtkSheetRange visible_range;
159 gint current_row, current_column;
161 gtk_notebook_set_current_page (GTK_NOTEBOOK(de), PSPPIRE_DATA_EDITOR_DATA_VIEW);
163 gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet[0]),
164 ¤t_row, ¤t_column);
166 gtk_sheet_set_active_cell (GTK_SHEET (de->data_sheet[0]), current_row, row);
168 gtk_sheet_get_visible_range (GTK_SHEET (de->data_sheet[0]), &visible_range);
170 if ( row < visible_range.col0 || row > visible_range.coli)
171 gtk_sheet_moveto (GTK_SHEET (de->data_sheet[0]), -1, row, 0.5, 0.5);
177 /* Moves the focus to a new cell.
178 Returns TRUE iff the move should be disallowed */
180 traverse_cell_callback (GtkSheet *sheet,
181 GtkSheetCell *existing_cell,
182 GtkSheetCell *new_cell,
185 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
186 const PsppireDict *dict = de->data_store->dict;
188 if ( new_cell->col >= psppire_dict_get_var_cnt (dict))
211 #define DEFAULT_ROW_HEIGHT 25
214 new_data_callback (PsppireDataStore *ds, gpointer data)
217 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
219 casenumber n_cases = psppire_data_store_get_case_count (ds);
221 for (i = 0; i < 2; ++i)
223 psppire_axis_impl_clear (de->vaxis[i]);
224 psppire_axis_impl_append_n (de->vaxis[i], n_cases, DEFAULT_ROW_HEIGHT);
229 case_inserted_callback (PsppireDataStore *ds, gint before, gpointer data)
232 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
234 for (i = 0; i < 2; ++i)
235 psppire_axis_impl_insert (de->vaxis[i], before, DEFAULT_ROW_HEIGHT);
240 cases_deleted_callback (PsppireDataStore *ds, gint first, gint n_cases, gpointer data)
243 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
245 for (i = 0; i < 2; ++i)
246 psppire_axis_impl_delete (de->vaxis[0], first, n_cases);
251 /* Return the width (in pixels) of an upper case M when rendered in the
255 width_of_m (GtkWidget *w)
258 PangoLayout *layout = gtk_widget_create_pango_layout (w, "M");
260 pango_layout_get_pixel_extents (layout, NULL, &rect);
262 g_object_unref (layout);
267 /* Callback for the axis' resize signal.
268 Changes the variable's display width */
270 rewidth_variable (GtkWidget *w, gint unit, glong size)
272 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (w);
274 const PsppireDict *dict = de->data_store->dict;
275 struct variable *var = psppire_dict_get_variable (dict, unit);
277 var_set_display_width (var, size / (float) width_of_m (w));
282 new_variables_callback (PsppireDict *dict, gpointer data)
285 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
286 gint m_width = width_of_m (GTK_WIDGET (de));
288 PsppireAxisImpl *vaxis;
289 g_object_get (de->var_sheet, "vertical-axis", &vaxis, NULL);
291 psppire_axis_impl_clear (vaxis);
292 psppire_axis_impl_append_n (vaxis, 1 + psppire_dict_get_var_cnt (dict), DEFAULT_ROW_HEIGHT);
294 g_signal_connect_swapped (de->haxis, "resize-unit",
295 G_CALLBACK (rewidth_variable), de);
297 psppire_axis_impl_clear (de->haxis);
299 for (v = 0 ; v < psppire_dict_get_var_cnt (dict); ++v)
301 const struct variable *var = psppire_dict_get_variable (dict, v);
303 psppire_axis_impl_append (de->haxis, m_width * var_get_display_width (var));
308 insert_variable_callback (PsppireDict *dict, gint x, gpointer data)
310 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
312 gint m_width = width_of_m (GTK_WIDGET (de));
314 PsppireAxisImpl *var_vaxis;
316 const struct variable *var = psppire_dict_get_variable (dict, x);
318 g_object_get (de->var_sheet, "vertical-axis", &var_vaxis, NULL);
320 psppire_axis_impl_insert (var_vaxis, x, DEFAULT_ROW_HEIGHT);
323 psppire_axis_impl_insert (de->haxis, x, m_width * var_get_display_width (var));
328 delete_variable_callback (PsppireDict *dict, gint posn,
329 gint x UNUSED, gint y UNUSED, gpointer data)
331 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
333 PsppireAxisImpl *var_vaxis;
334 g_object_get (de->var_sheet, "vertical-axis", &var_vaxis, NULL);
336 psppire_axis_impl_delete (var_vaxis, posn, 1);
338 psppire_axis_impl_delete (de->haxis, posn, 1);
343 rewidth_variable_callback (PsppireDict *dict, gint posn, gpointer data)
345 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
346 gint m_width = width_of_m (GTK_WIDGET (de));
348 const struct variable *var = psppire_dict_get_variable (dict, posn);
350 psppire_axis_impl_resize (de->haxis, posn, m_width *
351 var_get_display_width (var));
356 psppire_data_editor_set_property (GObject *object,
362 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
366 case PROP_SPLIT_WINDOW:
367 psppire_data_editor_split_window (de, g_value_get_boolean (value));
369 case PROP_DATA_STORE:
370 if ( de->data_store) g_object_unref (de->data_store);
371 de->data_store = g_value_get_pointer (value);
372 g_object_ref (de->data_store);
374 for (i = 0 ; i < 4 ; ++i )
375 g_object_set (de->data_sheet[i],
376 "model", de->data_store,
379 g_signal_connect (de->data_store->dict, "backend-changed",
380 G_CALLBACK (new_variables_callback), de);
382 g_signal_connect (de->data_store->dict, "variable-inserted",
383 G_CALLBACK (insert_variable_callback), de);
385 g_signal_connect (de->data_store->dict, "variable-deleted",
386 G_CALLBACK (delete_variable_callback), de);
388 g_signal_connect (de->data_store->dict, "variable-display-width-changed",
389 G_CALLBACK (rewidth_variable_callback), de);
391 g_signal_connect (de->data_store, "backend-changed",
392 G_CALLBACK (new_data_callback), de);
394 g_signal_connect (de->data_store, "case-inserted",
395 G_CALLBACK (case_inserted_callback), de);
397 g_signal_connect (de->data_store, "cases-deleted",
398 G_CALLBACK (cases_deleted_callback), de);
402 if ( de->var_store) g_object_unref (de->var_store);
403 de->var_store = g_value_get_pointer (value);
404 g_object_ref (de->var_store);
406 g_object_set (de->var_sheet,
407 "model", de->var_store,
410 case PROP_VS_ROW_MENU:
412 GObject *menu = g_value_get_object (value);
414 g_signal_connect (de->var_sheet, "button-event-row",
415 G_CALLBACK (popup_variable_row_menu), menu);
418 case PROP_DS_COLUMN_MENU:
420 GObject *menu = g_value_get_object (value);
422 g_signal_connect (de->data_sheet[0], "button-event-column",
423 G_CALLBACK (popup_variable_column_menu), menu);
426 case PROP_DS_ROW_MENU:
428 GObject *menu = g_value_get_object (value);
430 g_signal_connect (de->data_sheet[0], "button-event-row",
431 G_CALLBACK (popup_cases_menu), menu);
434 case PROP_CURRENT_VAR:
437 gint var = g_value_get_long (value);
438 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (object)))
440 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
441 gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet[0]), &row, &col);
442 gtk_sheet_set_active_cell (GTK_SHEET (de->data_sheet[0]), row, var);
443 gtk_sheet_moveto (GTK_SHEET (de->data_sheet[0]), -1, var, 0.5, 0.5);
445 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
446 gtk_sheet_get_active_cell (GTK_SHEET (de->var_sheet), &row, &col);
447 gtk_sheet_set_active_cell (GTK_SHEET (de->var_sheet), var, col);
448 gtk_sheet_moveto (GTK_SHEET (de->var_sheet), var, -1, 0.5, 0.5);
451 g_assert_not_reached ();
456 case PROP_CURRENT_CASE:
459 gint case_num = g_value_get_long (value);
460 gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet[0]), &row, &col);
461 gtk_sheet_set_active_cell (GTK_SHEET (de->data_sheet[0]), case_num, col);
462 gtk_sheet_moveto (GTK_SHEET (de->data_sheet[0]), case_num, -1, 0.5, 0.5);
465 case PROP_VALUE_LABELS:
467 psppire_data_store_show_labels (de->data_store,
468 g_value_get_boolean (value));
472 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
478 psppire_data_editor_get_property (GObject *object,
483 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
487 case PROP_SPLIT_WINDOW:
488 g_value_set_boolean (value, de->split);
490 case PROP_DATA_STORE:
491 g_value_set_pointer (value, de->data_store);
494 g_value_set_pointer (value, de->var_store);
496 case PROP_CURRENT_CASE:
499 gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet[0]), &row, &column);
500 g_value_set_long (value, row);
503 case PROP_CURRENT_VAR:
506 gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet[0]), &row, &column);
507 g_value_set_long (value, column);
510 case PROP_DATA_SELECTED:
511 g_value_set_boolean (value, data_is_selected (de));
514 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
521 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
523 GParamSpec *data_store_spec ;
524 GParamSpec *var_store_spec ;
525 GParamSpec *column_menu_spec;
526 GParamSpec *ds_row_menu_spec;
527 GParamSpec *vs_row_menu_spec;
528 GParamSpec *value_labels_spec;
529 GParamSpec *current_case_spec;
530 GParamSpec *current_var_spec;
531 GParamSpec *data_selected_spec;
532 GParamSpec *split_window_spec;
533 GObjectClass *object_class = G_OBJECT_CLASS (klass);
535 parent_class = g_type_class_peek_parent (klass);
537 object_class->dispose = psppire_data_editor_dispose;
538 object_class->finalize = psppire_data_editor_finalize;
540 object_class->set_property = psppire_data_editor_set_property;
541 object_class->get_property = psppire_data_editor_get_property;
544 g_param_spec_pointer ("data-store",
546 "A pointer to the data store associated with this editor",
547 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
549 g_object_class_install_property (object_class,
554 g_param_spec_pointer ("var-store",
556 "A pointer to the variable store associated with this editor",
557 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
559 g_object_class_install_property (object_class,
564 g_param_spec_object ("datasheet-column-menu",
565 "Data Sheet Column Menu",
566 "A menu to be displayed when button 3 is pressed in thedata sheet's column title buttons",
570 g_object_class_install_property (object_class,
576 g_param_spec_object ("datasheet-row-menu",
577 "Data Sheet Row Menu",
578 "A menu to be displayed when button 3 is pressed in the data sheet's row title buttons",
582 g_object_class_install_property (object_class,
588 g_param_spec_object ("varsheet-row-menu",
589 "Variable Sheet Row Menu",
590 "A menu to be displayed when button 3 is pressed in the variable sheet's row title buttons",
594 g_object_class_install_property (object_class,
600 g_param_spec_boolean ("value-labels",
602 "Whether or not the data sheet should display labels instead of values",
604 G_PARAM_WRITABLE | G_PARAM_READABLE);
606 g_object_class_install_property (object_class,
612 g_param_spec_long ("current-case",
614 "Zero based number of the selected case",
617 G_PARAM_WRITABLE | G_PARAM_READABLE);
619 g_object_class_install_property (object_class,
625 g_param_spec_long ("current-variable",
627 "Zero based number of the selected variable",
630 G_PARAM_WRITABLE | G_PARAM_READABLE);
632 g_object_class_install_property (object_class,
638 g_param_spec_boolean ("data-selected",
640 "True iff the data view is active and one or more cells of data have been selected.",
644 g_object_class_install_property (object_class,
651 g_param_spec_boolean ("split",
653 "True iff the data sheet is split",
655 G_PARAM_READABLE | G_PARAM_WRITABLE);
657 g_object_class_install_property (object_class,
661 data_editor_signals [DATA_SELECTION_CHANGED] =
662 g_signal_new ("data-selection-changed",
663 G_TYPE_FROM_CLASS (klass),
667 g_cclosure_marshal_VOID__BOOLEAN,
672 data_editor_signals [CASES_SELECTED] =
673 g_signal_new ("cases-selected",
674 G_TYPE_FROM_CLASS (klass),
678 g_cclosure_marshal_VOID__INT,
684 data_editor_signals [VARIABLES_SELECTED] =
685 g_signal_new ("variables-selected",
686 G_TYPE_FROM_CLASS (klass),
690 g_cclosure_marshal_VOID__INT,
696 data_editor_signals [DATA_AVAILABLE_CHANGED] =
697 g_signal_new ("data-available-changed",
698 G_TYPE_FROM_CLASS (klass),
702 g_cclosure_marshal_VOID__BOOLEAN,
708 /* Update the data_ref_entry with the reference of the active cell */
710 update_data_ref_entry (const GtkSheet *sheet,
712 gint old_row, gint old_col,
715 PsppireDataEditor *de = data;
717 PsppireDataStore *data_store =
718 PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
722 const struct variable *var =
723 psppire_dict_get_variable (data_store->dict, col);
727 gchar *text = g_strdup_printf ("%d: %s", row + FIRST_CASE_NUMBER,
730 gchar *s = pspp_locale_to_utf8 (text, -1, 0);
734 gtk_entry_set_text (GTK_ENTRY (de->cell_ref_entry), s);
744 psppire_data_store_get_string (data_store, row,
745 var_get_dict_index(var));
752 gtk_entry_set_text (GTK_ENTRY (de->datum_entry), text);
764 gtk_entry_set_text (GTK_ENTRY (de->datum_entry), "");
771 datum_entry_activate (GtkEntry *entry, gpointer data)
774 PsppireDataEditor *de = data;
776 const gchar *text = gtk_entry_get_text (entry);
778 gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet[0]), &row, &column);
780 if ( row == -1 || column == -1)
783 psppire_data_store_set_string (de->data_store, text, row, column);
786 static void on_activate (PsppireDataEditor *de);
787 static gboolean on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p, gint pagenum, gpointer data);
788 static void on_select_range (PsppireDataEditor *de);
790 static void on_select_row (GtkSheet *, gint, PsppireDataEditor *);
791 static void on_select_variable (GtkSheet *, gint, PsppireDataEditor *);
794 static void on_owner_change (GtkClipboard *,
795 GdkEventOwnerChange *, gpointer);
798 on_map (GtkWidget *w)
800 GtkClipboard *clip = gtk_widget_get_clipboard (w, GDK_SELECTION_CLIPBOARD);
802 g_signal_connect (clip, "owner-change", G_CALLBACK (on_owner_change), w);
807 init_sheet (PsppireDataEditor *de, int i,
808 GtkAdjustment *hadj, GtkAdjustment *vadj,
809 PsppireAxisImpl *vaxis,
810 PsppireAxisImpl *haxis
813 de->sheet_bin[i] = gtk_scrolled_window_new (hadj, vadj);
815 de->data_sheet[i] = gtk_sheet_new (NULL);
817 g_object_set (de->sheet_bin[i],
819 "shadow-type", GTK_SHADOW_ETCHED_IN,
822 g_object_set (haxis, "default-size", 75, NULL);
823 g_object_set (vaxis, "default-size", 25, NULL);
825 g_object_set (de->data_sheet[i],
826 "horizontal-axis", haxis,
827 "vertical-axis", vaxis,
830 gtk_container_add (GTK_CONTAINER (de->sheet_bin[i]), de->data_sheet[i]);
832 g_signal_connect (de->data_sheet[i], "traverse",
833 G_CALLBACK (traverse_cell_callback), de);
835 gtk_widget_show (de->sheet_bin[i]);
840 init_data_sheet (PsppireDataEditor *de)
842 GtkAdjustment *vadj0, *hadj0;
843 GtkAdjustment *vadj1, *hadj1;
846 de->vaxis[0] = psppire_axis_impl_new ();
847 de->vaxis[1] = psppire_axis_impl_new ();
849 /* Txoxovhere's only one horizontal axis, since the
850 column widths are parameters of the variables */
851 de->haxis = psppire_axis_impl_new ();
855 de->paned = gtk_xpaned_new ();
857 init_sheet (de, 0, NULL, NULL, de->vaxis[0], de->haxis);
858 gtk_widget_show (de->sheet_bin[0]);
859 vadj0 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
860 hadj0 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
862 g_object_set (de->sheet_bin[0], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
863 g_object_set (de->sheet_bin[0], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
865 init_sheet (de, 1, NULL, vadj0, de->vaxis[0], de->haxis);
866 gtk_widget_show (de->sheet_bin[1]);
867 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[1]));
868 gtk_sheet_hide_row_titles (GTK_SHEET (sheet));
869 hadj1 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[1]));
870 g_object_set (de->sheet_bin[1], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
871 g_object_set (de->sheet_bin[1], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
873 init_sheet (de, 2, hadj0, NULL, de->vaxis[1], de->haxis);
874 gtk_widget_show (de->sheet_bin[2]);
875 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[2]));
876 gtk_sheet_hide_column_titles (GTK_SHEET (sheet));
877 g_object_set (de->sheet_bin[2], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
878 g_object_set (de->sheet_bin[2], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
879 vadj1 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[2]));
881 init_sheet (de, 3, hadj1, vadj1, de->vaxis[1], de->haxis);
882 gtk_widget_show (de->sheet_bin[3]);
883 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[3]));
884 gtk_sheet_hide_column_titles (GTK_SHEET (sheet));
885 gtk_sheet_hide_row_titles (GTK_SHEET (sheet));
886 g_object_set (de->sheet_bin[3], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
887 g_object_set (de->sheet_bin[3], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
889 gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin[0], TRUE, TRUE);
890 gtk_xpaned_pack_top_right (GTK_XPANED (de->paned), de->sheet_bin[1], TRUE, TRUE);
891 gtk_xpaned_pack_bottom_left (GTK_XPANED (de->paned), de->sheet_bin[2], TRUE, TRUE);
892 gtk_xpaned_pack_bottom_right (GTK_XPANED (de->paned), de->sheet_bin[3], TRUE, TRUE);
894 gtk_xpaned_set_position_y (GTK_XPANED (de->paned), 150);
895 gtk_xpaned_set_position_x (GTK_XPANED (de->paned), 350);
900 psppire_data_editor_init (PsppireDataEditor *de)
902 GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
903 GtkWidget *sw_vs = gtk_scrolled_window_new (NULL, NULL);
905 init_data_sheet (de);
907 de->data_vbox = gtk_vbox_new (FALSE, 0);
908 de->var_sheet = psppire_var_sheet_new ();
910 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
912 de->datum_entry = gtk_entry_new ();
913 de->cell_ref_entry = gtk_entry_new ();
915 g_object_set (de->cell_ref_entry,
921 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_entry, FALSE, FALSE, 0);
922 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
925 gtk_container_add (GTK_CONTAINER (sw_vs), de->var_sheet);
926 gtk_widget_show_all (sw_vs);
929 gtk_box_pack_start (GTK_BOX (de->data_vbox), hbox, FALSE, FALSE, 0);
930 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned, TRUE, TRUE, 0);
933 psppire_data_editor_remove_split (de);
935 gtk_widget_show_all (de->data_vbox);
937 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->data_vbox,
938 gtk_label_new_with_mnemonic (_("Data View")));
940 gtk_notebook_append_page (GTK_NOTEBOOK (de), sw_vs,
941 gtk_label_new_with_mnemonic (_("Variable View")));
943 g_signal_connect (de->data_sheet[0], "activate",
944 G_CALLBACK (update_data_ref_entry),
947 g_signal_connect (de->datum_entry, "activate",
948 G_CALLBACK (datum_entry_activate),
952 g_signal_connect_swapped (de->data_sheet[0],
953 "double-click-column",
954 G_CALLBACK (on_data_column_clicked),
957 g_signal_connect_swapped (de->var_sheet,
959 G_CALLBACK (on_var_row_clicked),
962 g_signal_connect_swapped (de->data_sheet[0], "activate",
963 G_CALLBACK (on_activate),
966 g_signal_connect_swapped (de->data_sheet[0], "select-range",
967 G_CALLBACK (on_select_range),
970 g_signal_connect (de->data_sheet[0], "select-row",
971 G_CALLBACK (on_select_row), de);
973 g_signal_connect (de->data_sheet[0], "select-column",
974 G_CALLBACK (on_select_variable), de);
977 g_signal_connect (de->var_sheet, "select-row",
978 G_CALLBACK (on_select_variable), de);
981 g_signal_connect_after (de, "switch-page",
982 G_CALLBACK (on_switch_page),
986 g_signal_connect (de, "map", G_CALLBACK (on_map), NULL);
990 // gtk_sheet_hide_column_titles (de->var_sheet);
991 // gtk_sheet_hide_row_titles (de->data_sheet);
994 de->dispose_has_run = FALSE;
999 psppire_data_editor_new (PsppireVarStore *var_store,
1000 PsppireDataStore *data_store)
1002 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
1003 "var-store", var_store,
1004 "data-store", data_store,
1010 psppire_data_editor_remove_split (PsppireDataEditor *de)
1012 if ( !de->split ) return;
1015 g_object_ref (de->sheet_bin[0]);
1016 gtk_container_remove (GTK_CONTAINER (de->paned), de->sheet_bin[0]);
1018 g_object_ref (de->paned);
1019 gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->paned);
1021 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->sheet_bin[0],
1024 g_object_unref (de->sheet_bin[0]);
1026 g_object_set (de->sheet_bin[0], "vscrollbar-policy",
1027 GTK_POLICY_ALWAYS, NULL);
1029 g_object_set (de->sheet_bin[0], "hscrollbar-policy",
1030 GTK_POLICY_ALWAYS, NULL);
1035 psppire_data_editor_set_split (PsppireDataEditor *de)
1037 if ( de->split ) return;
1040 g_object_ref (de->sheet_bin[0]);
1041 gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->sheet_bin[0]);
1043 gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin [0],
1046 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned,
1049 g_object_unref (de->paned);
1051 g_object_set (de->sheet_bin[0], "vscrollbar-policy",
1052 GTK_POLICY_NEVER, NULL);
1054 g_object_set (de->sheet_bin[0], "hscrollbar-policy",
1055 GTK_POLICY_NEVER, NULL);
1059 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
1062 psppire_data_editor_set_split (de);
1064 psppire_data_editor_remove_split (de);
1066 gtk_widget_show_all (de->data_vbox);
1069 static void data_sheet_set_clip (GtkSheet *sheet);
1070 static void data_sheet_contents_received_callback (GtkClipboard *clipboard,
1071 GtkSelectionData *sd,
1076 psppire_data_editor_clip_copy (PsppireDataEditor *de)
1078 data_sheet_set_clip (GTK_SHEET (de->data_sheet[0]));
1082 psppire_data_editor_clip_paste (PsppireDataEditor *de)
1084 GdkDisplay *display = gtk_widget_get_display ( GTK_WIDGET (de));
1085 GtkClipboard *clipboard =
1086 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1088 gtk_clipboard_request_contents (clipboard,
1089 gdk_atom_intern ("UTF8_STRING", TRUE),
1090 data_sheet_contents_received_callback,
1097 psppire_data_editor_clip_cut (PsppireDataEditor *de)
1099 gint max_rows, max_columns;
1101 GtkSheetRange range;
1102 PsppireDataStore *ds = de->data_store;
1104 data_sheet_set_clip (GTK_SHEET (de->data_sheet[0]));
1106 /* Now blank all the cells */
1107 gtk_sheet_get_selected_range (GTK_SHEET (de->data_sheet[0]), &range);
1109 /* If nothing selected, then use active cell */
1110 if ( range.row0 < 0 || range.col0 < 0 )
1113 gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet[0]), &row, &col);
1115 range.row0 = range.rowi = row;
1116 range.col0 = range.coli = col;
1119 /* The sheet range can include cells that do not include data.
1120 Exclude them from the range. */
1121 max_rows = psppire_data_store_get_case_count (ds);
1122 if (range.rowi >= max_rows)
1126 range.rowi = max_rows - 1;
1129 max_columns = dict_get_var_cnt (ds->dict->dict);
1130 if (range.coli >= max_columns)
1132 if (max_columns == 0)
1134 range.coli = max_columns - 1;
1137 g_return_if_fail (range.rowi >= range.row0);
1138 g_return_if_fail (range.row0 >= 0);
1139 g_return_if_fail (range.coli >= range.col0);
1140 g_return_if_fail (range.col0 >= 0);
1143 for (r = range.row0; r <= range.rowi ; ++r )
1147 for (c = range.col0 ; c <= range.coli; ++c)
1149 psppire_data_store_set_string (ds, "", r, c);
1153 /* and remove the selection */
1154 gtk_sheet_unselect_range (GTK_SHEET (de->data_sheet[0]));
1160 /* Popup menu related stuff */
1163 popup_variable_column_menu (GtkSheet *sheet, gint column,
1164 GdkEventButton *event, gpointer data)
1166 GtkMenu *menu = GTK_MENU (data);
1168 PsppireDataStore *data_store =
1169 PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1171 const struct variable *v =
1172 psppire_dict_get_variable (data_store->dict, column);
1174 if ( v && event->button == 3)
1176 gtk_sheet_select_column (sheet, column);
1178 gtk_menu_popup (menu,
1179 NULL, NULL, NULL, NULL,
1180 event->button, event->time);
1186 popup_variable_row_menu (GtkSheet *sheet, gint row,
1187 GdkEventButton *event, gpointer data)
1189 GtkMenu *menu = GTK_MENU (data);
1191 PsppireVarStore *var_store =
1192 PSPPIRE_VAR_STORE (gtk_sheet_get_model (sheet));
1194 const struct variable *v =
1195 psppire_dict_get_variable (var_store->dict, row);
1197 if ( v && event->button == 3)
1199 gtk_sheet_select_row (sheet, row);
1201 gtk_menu_popup (menu,
1202 NULL, NULL, NULL, NULL,
1203 event->button, event->time);
1209 popup_cases_menu (GtkSheet *sheet, gint row,
1210 GdkEventButton *event, gpointer data)
1212 GtkMenu *menu = GTK_MENU (data);
1214 PsppireDataStore *data_store =
1215 PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1217 if ( row <= psppire_data_store_get_case_count (data_store) &&
1220 gtk_sheet_select_row (sheet, row);
1222 gtk_menu_popup (menu,
1223 NULL, NULL, NULL, NULL,
1224 event->button, event->time);
1233 do_sort (PsppireDataStore *ds, int var, gboolean descend)
1235 GString *string = g_string_new ("SORT CASES BY ");
1237 const struct variable *v =
1238 psppire_dict_get_variable (ds->dict, var);
1240 g_string_append_printf (string, "%s", var_get_name (v));
1243 g_string_append (string, " (D)");
1245 g_string_append (string, ".");
1247 execute_syntax (create_syntax_string_source (string->str));
1249 g_string_free (string, TRUE);
1253 /* Sort the data by the the variable which the editor has currently
1256 psppire_data_editor_sort_ascending (PsppireDataEditor *de)
1258 GtkSheetRange range;
1259 gtk_sheet_get_selected_range (GTK_SHEET(de->data_sheet[0]), &range);
1261 do_sort (de->data_store, range.col0, FALSE);
1265 /* Sort the data by the the variable which the editor has currently
1268 psppire_data_editor_sort_descending (PsppireDataEditor *de)
1270 GtkSheetRange range;
1271 gtk_sheet_get_selected_range (GTK_SHEET(de->data_sheet[0]), &range);
1273 do_sort (de->data_store, range.col0, TRUE);
1280 /* Insert a new variable before the currently selected position */
1282 psppire_data_editor_insert_variable (PsppireDataEditor *de)
1286 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
1288 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
1289 if ( de->data_sheet[0]->state == GTK_SHEET_COLUMN_SELECTED )
1290 posn = GTK_SHEET (de->data_sheet[0])->range.col0;
1292 posn = GTK_SHEET (de->data_sheet[0])->active_cell.col;
1294 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
1295 if ( de->var_sheet->state == GTK_SHEET_ROW_SELECTED )
1296 posn = GTK_SHEET (de->var_sheet)->range.row0;
1298 posn = GTK_SHEET (de->var_sheet)->active_cell.row;
1301 g_assert_not_reached ();
1305 psppire_dict_insert_variable (de->data_store->dict, posn, NULL);
1308 /* Insert a new case before the currently selected position */
1310 psppire_data_editor_insert_case (PsppireDataEditor *de)
1314 if ( de->data_sheet[0]->state == GTK_SHEET_ROW_SELECTED )
1315 posn = GTK_SHEET (de->data_sheet[0])->range.row0;
1317 posn = GTK_SHEET (de->data_sheet[0])->active_cell.row;
1319 if ( posn == -1 ) posn = 0;
1321 psppire_data_store_insert_new_case (de->data_store, posn);
1324 /* Delete the cases currently selected in the data sheet */
1326 psppire_data_editor_delete_cases (PsppireDataEditor *de)
1328 gint first = GTK_SHEET (de->data_sheet[0])->range.row0;
1329 gint n = GTK_SHEET (de->data_sheet[0])->range.rowi - first + 1;
1331 psppire_data_store_delete_cases (de->data_store, first, n);
1333 gtk_sheet_unselect_range (GTK_SHEET (de->data_sheet[0]));
1336 /* Delete the variables currently selected in the
1337 datasheet or variable sheet */
1339 psppire_data_editor_delete_variables (PsppireDataEditor *de)
1343 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
1345 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
1346 first = GTK_SHEET (de->data_sheet[0])->range.col0;
1347 n = GTK_SHEET (de->data_sheet[0])->range.coli - first + 1;
1349 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
1350 first = GTK_SHEET (de->var_sheet)->range.row0;
1351 n = GTK_SHEET (de->var_sheet)->range.rowi - first + 1;
1354 g_assert_not_reached ();
1358 psppire_dict_delete_variables (de->var_store->dict, first, n);
1360 gtk_sheet_unselect_range (GTK_SHEET (de->data_sheet[0]));
1361 gtk_sheet_unselect_range (GTK_SHEET (de->var_sheet));
1366 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
1368 gtk_sheet_show_grid (GTK_SHEET (de->var_sheet), grid_visible);
1369 gtk_sheet_show_grid (GTK_SHEET (de->data_sheet[0]), grid_visible);
1374 set_font (GtkWidget *w, gpointer data)
1376 PangoFontDescription *font_desc = data;
1377 GtkRcStyle *style = gtk_widget_get_modifier_style (w);
1379 pango_font_description_free (style->font_desc);
1380 style->font_desc = pango_font_description_copy (font_desc);
1382 gtk_widget_modify_style (w, style);
1384 if ( GTK_IS_CONTAINER (w))
1385 gtk_container_foreach (GTK_CONTAINER (w), set_font, font_desc);
1389 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
1391 set_font (GTK_WIDGET (de), font_desc);
1392 gtk_container_foreach (GTK_CONTAINER (de), set_font, font_desc);
1400 emit_selected_signal (PsppireDataEditor *de)
1402 gboolean data_selected = data_is_selected (de);
1404 g_signal_emit (de, data_editor_signals[DATA_SELECTION_CHANGED], 0, data_selected);
1409 on_activate (PsppireDataEditor *de)
1412 gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet[0]), &row, &col);
1415 if ( row < psppire_data_store_get_case_count (de->data_store)
1417 col < psppire_var_store_get_var_cnt (de->var_store))
1419 emit_selected_signal (de);
1423 emit_selected_signal (de);
1428 on_select_range (PsppireDataEditor *de)
1430 GtkSheetRange range;
1432 gtk_sheet_get_selected_range (GTK_SHEET (de->data_sheet[0]), &range);
1434 if ( range.rowi < psppire_data_store_get_case_count (de->data_store)
1436 range.coli < psppire_var_store_get_var_cnt (de->var_store))
1438 emit_selected_signal (de);
1442 emit_selected_signal (de);
1447 on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p,
1448 gint pagenum, gpointer data)
1450 if ( pagenum != PSPPIRE_DATA_EDITOR_DATA_VIEW )
1452 emit_selected_signal (de);
1456 on_select_range (de);
1464 data_is_selected (PsppireDataEditor *de)
1466 GtkSheetRange range;
1469 if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (de)) != PSPPIRE_DATA_EDITOR_DATA_VIEW)
1472 gtk_sheet_get_active_cell (GTK_SHEET (de->data_sheet[0]), &row, &col);
1474 if ( row >= psppire_data_store_get_case_count (de->data_store)
1476 col >= psppire_var_store_get_var_cnt (de->var_store))
1481 gtk_sheet_get_selected_range (GTK_SHEET (de->data_sheet[0]), &range);
1483 if ( range.rowi >= psppire_data_store_get_case_count (de->data_store)
1485 range.coli >= psppire_var_store_get_var_cnt (de->var_store))
1495 on_select_row (GtkSheet *sheet, gint row, PsppireDataEditor *de)
1497 g_signal_emit (de, data_editor_signals[CASES_SELECTED], 0, row);
1502 on_select_variable (GtkSheet *sheet, gint var, PsppireDataEditor *de)
1504 g_signal_emit (de, data_editor_signals[VARIABLES_SELECTED], 0, var);
1510 /* Clipboard stuff */
1513 #include <data/casereader.h>
1514 #include <data/case-map.h>
1515 #include <data/casewriter.h>
1517 #include <data/data-out.h>
1520 /* A casereader and dictionary holding the data currently in the clip */
1521 static struct casereader *clip_datasheet = NULL;
1522 static struct dictionary *clip_dict = NULL;
1525 static void data_sheet_update_clipboard (GtkSheet *);
1527 /* Set the clip according to the currently
1528 selected range in the data sheet */
1530 data_sheet_set_clip (GtkSheet *sheet)
1533 struct casewriter *writer ;
1534 GtkSheetRange range;
1535 PsppireDataStore *ds;
1536 struct case_map *map = NULL;
1537 casenumber max_rows;
1540 ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1542 gtk_sheet_get_selected_range (sheet, &range);
1544 /* If nothing selected, then use active cell */
1545 if ( range.row0 < 0 || range.col0 < 0 )
1548 gtk_sheet_get_active_cell (sheet, &row, &col);
1550 range.row0 = range.rowi = row;
1551 range.col0 = range.coli = col;
1554 /* The sheet range can include cells that do not include data.
1555 Exclude them from the range. */
1556 max_rows = psppire_data_store_get_case_count (ds);
1557 if (range.rowi >= max_rows)
1561 range.rowi = max_rows - 1;
1563 max_columns = dict_get_var_cnt (ds->dict->dict);
1564 if (range.coli >= max_columns)
1566 if (max_columns == 0)
1568 range.coli = max_columns - 1;
1571 g_return_if_fail (range.rowi >= range.row0);
1572 g_return_if_fail (range.row0 >= 0);
1573 g_return_if_fail (range.coli >= range.col0);
1574 g_return_if_fail (range.col0 >= 0);
1576 /* Destroy any existing clip */
1577 if ( clip_datasheet )
1579 casereader_destroy (clip_datasheet);
1580 clip_datasheet = NULL;
1585 dict_destroy (clip_dict);
1589 /* Construct clip dictionary. */
1590 clip_dict = dict_create ();
1591 for (i = range.col0; i <= range.coli; i++)
1593 const struct variable *old = dict_get_var (ds->dict->dict, i);
1594 dict_clone_var_assert (clip_dict, old, var_get_name (old));
1597 /* Construct clip data. */
1598 map = case_map_by_name (ds->dict->dict, clip_dict);
1599 writer = autopaging_writer_create (dict_get_next_value_idx (clip_dict));
1600 for (i = range.row0; i <= range.rowi ; ++i )
1604 if (psppire_data_store_get_case (ds, i, &old))
1608 case_map_execute (map, &old, &new);
1609 case_destroy (&old);
1610 casewriter_write (writer, &new);
1613 casewriter_force_error (writer);
1615 case_map_destroy (map);
1617 clip_datasheet = casewriter_make_reader (writer);
1619 data_sheet_update_clipboard (sheet);
1629 /* Perform data_out for case CC, variable V, appending to STRING */
1631 data_out_g_string (GString *string, const struct variable *v,
1632 const struct ccase *cc)
1636 const struct fmt_spec *fs = var_get_print_format (v);
1637 const union value *val = case_data (cc, v);
1638 buf = xzalloc (fs->w);
1640 data_out (val, fs, buf);
1642 g_string_append_len (string, buf, fs->w);
1653 const size_t val_cnt = casereader_get_value_cnt (clip_datasheet);
1654 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
1655 const size_t var_cnt = dict_get_var_cnt (clip_dict);
1657 string = g_string_sized_new (10 * val_cnt * case_cnt);
1659 for (r = 0 ; r < case_cnt ; ++r )
1663 if ( ! casereader_peek (clip_datasheet, r, &cc))
1665 g_warning ("Clipboard seems to have inexplicably shrunk");
1669 for (c = 0 ; c < var_cnt ; ++c)
1671 const struct variable *v = dict_get_var (clip_dict, c);
1672 data_out_g_string (string, v, &cc);
1673 if ( c < val_cnt - 1 )
1674 g_string_append (string, "\t");
1678 g_string_append (string, "\n");
1693 const size_t val_cnt = casereader_get_value_cnt (clip_datasheet);
1694 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
1695 const size_t var_cnt = dict_get_var_cnt (clip_dict);
1698 /* Guestimate the size needed */
1699 string = g_string_sized_new (20 * val_cnt * case_cnt);
1701 g_string_append (string, "<table>\n");
1702 for (r = 0 ; r < case_cnt ; ++r )
1706 if ( ! casereader_peek (clip_datasheet, r, &cc))
1708 g_warning ("Clipboard seems to have inexplicably shrunk");
1711 g_string_append (string, "<tr>\n");
1713 for (c = 0 ; c < var_cnt ; ++c)
1715 const struct variable *v = dict_get_var (clip_dict, c);
1716 g_string_append (string, "<td>");
1717 data_out_g_string (string, v, &cc);
1718 g_string_append (string, "</td>\n");
1721 g_string_append (string, "</tr>\n");
1725 g_string_append (string, "</table>\n");
1733 clipboard_get_cb (GtkClipboard *clipboard,
1734 GtkSelectionData *selection_data,
1738 GString *string = NULL;
1742 case SELECT_FMT_TEXT:
1743 string = clip_to_text ();
1745 case SELECT_FMT_HTML:
1746 string = clip_to_html ();
1749 g_assert_not_reached ();
1752 gtk_selection_data_set (selection_data, selection_data->target,
1754 (const guchar *) string->str, string->len);
1756 g_string_free (string, TRUE);
1760 clipboard_clear_cb (GtkClipboard *clipboard,
1763 dict_destroy (clip_dict);
1766 casereader_destroy (clip_datasheet);
1767 clip_datasheet = NULL;
1771 static const GtkTargetEntry targets[] = {
1772 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
1773 { "STRING", 0, SELECT_FMT_TEXT },
1774 { "TEXT", 0, SELECT_FMT_TEXT },
1775 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
1776 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
1777 { "text/plain", 0, SELECT_FMT_TEXT },
1778 { "text/html", 0, SELECT_FMT_HTML }
1784 data_sheet_update_clipboard (GtkSheet *sheet)
1786 GtkClipboard *clipboard =
1787 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
1788 GDK_SELECTION_CLIPBOARD);
1790 if (!gtk_clipboard_set_with_owner (clipboard, targets,
1791 G_N_ELEMENTS (targets),
1792 clipboard_get_cb, clipboard_clear_cb,
1794 clipboard_clear_cb (clipboard, sheet);
1799 /* A callback for when the clipboard contents have been received */
1801 data_sheet_contents_received_callback (GtkClipboard *clipboard,
1802 GtkSelectionData *sd,
1807 gint next_row, next_column;
1810 PsppireDataEditor *data_editor = data;
1812 if ( sd->length < 0 )
1815 if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
1818 c = (char *) sd->data;
1820 /* Paste text to selected position */
1821 gtk_sheet_get_active_cell (GTK_SHEET (data_editor->data_sheet[0]),
1824 g_return_if_fail (row >= 0);
1825 g_return_if_fail (column >= 0);
1827 first_column = column;
1829 next_column = column;
1830 while (count < sd->length)
1835 column = next_column;
1836 while (*c != '\t' && *c != '\n' && count < sd->length)
1844 next_column = column + 1;
1846 else if ( *c == '\n')
1849 next_column = first_column;
1855 /* Append some new cases if pasting beyond the last row */
1856 if ( row >= psppire_data_store_get_case_count (data_editor->data_store))
1857 psppire_data_store_insert_new_case (data_editor->data_store, row);
1859 psppire_data_store_set_string (data_editor->data_store, s, row, column);
1865 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
1868 gboolean compatible_target = FALSE;
1869 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
1871 for (i = 0 ; i < sizeof (targets) / sizeof(targets[0]) ; ++i )
1873 GdkAtom atom = gdk_atom_intern (targets[i].target, TRUE);
1874 if ( gtk_clipboard_wait_is_target_available (clip, atom))
1876 compatible_target = TRUE;
1881 g_signal_emit (de, data_editor_signals[DATA_AVAILABLE_CHANGED], 0,