1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013 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/>. */
19 #include "ui/gui/psppire-data-sheet.h"
21 #include "data/case-map.h"
22 #include "data/casereader.h"
23 #include "data/casewriter.h"
24 #include "data/data-out.h"
25 #include "data/datasheet.h"
26 #include "data/format.h"
27 #include "data/value-labels.h"
28 #include "libpspp/intern.h"
29 #include "libpspp/range-set.h"
30 #include "ui/gui/executor.h"
31 #include "ui/gui/find-dialog.h"
32 #include "ui/gui/goto-case-dialog.h"
33 #include "ui/gui/builder-wrapper.h"
34 #include "ui/gui/helper.h"
35 #include "ui/gui/pspp-sheet-selection.h"
36 #include "ui/gui/psppire-cell-renderer-button.h"
37 #include "ui/gui/psppire-data-store.h"
38 #include "ui/gui/psppire-data-window.h"
39 #include "ui/gui/psppire-dialog-action-var-info.h"
40 #include "ui/gui/psppire-empty-list-store.h"
41 #include "ui/gui/psppire-marshal.h"
43 #include "gl/intprops.h"
44 #include "gl/xalloc.h"
47 #define _(msgid) gettext (msgid)
48 #define N_(msgid) msgid
50 static void psppire_data_sheet_dispose (GObject *);
51 static void psppire_data_sheet_unset_data_store (PsppireDataSheet *);
53 static void psppire_data_sheet_update_clip_actions (PsppireDataSheet *);
54 static void psppire_data_sheet_update_primary_selection (PsppireDataSheet *,
56 static void psppire_data_sheet_set_clip (PsppireDataSheet *, gboolean cut);
58 static void on_selection_changed (PsppSheetSelection *, gpointer);
59 static void on_owner_change (GtkClipboard *, GdkEventOwnerChange *, gpointer);
60 static void psppire_data_sheet_clip_received_cb (GtkClipboard *,
61 GtkSelectionData *, gpointer);
63 G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, PSPP_TYPE_SHEET_VIEW);
66 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
68 size_t *row, PsppSheetViewColumn **columnp)
70 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
74 PsppSheetViewColumn *tree_column;
75 GtkTreeModel *tree_model;
78 /* Check that WIDGET is really visible on the screen before we
79 do anything else. This is a bug fix for a sticky situation:
80 when text_data_import_assistant() returns, it frees the data
81 necessary to compose the tool tip message, but there may be
82 a tool tip under preparation at that point (even if there is
83 no visible tool tip) that will call back into us a little
84 bit later. Perhaps the correct solution to this problem is
85 to make the data related to the tool tips part of a GObject
86 that only gets destroyed when all references are released,
87 but this solution appears to be effective too. */
88 if (!gtk_widget_get_mapped (widget))
91 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
93 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
94 &path, &tree_column, NULL, NULL))
97 *columnp = tree_column;
99 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
102 tree_model = pspp_sheet_view_get_model (tree_view);
103 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
104 gtk_tree_path_free (path);
108 *row = GPOINTER_TO_INT (iter.user_data);
113 on_query_tooltip (GtkWidget *widget, gint wx, gint wy,
114 gboolean keyboard_mode UNUSED,
115 GtkTooltip *tooltip, gpointer data UNUSED)
117 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
118 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
119 PsppSheetViewColumn *column;
120 struct variable *var;
126 g_return_val_if_fail (data_store != NULL, FALSE);
128 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
131 var = g_object_get_data (G_OBJECT (column), "variable");
134 if (g_object_get_data (G_OBJECT (column), "new-var-column") == NULL)
137 gtk_tooltip_set_text (tooltip,
138 _("Enter a number to add a new variable."));
141 else if (row >= datasheet_get_n_rows (data_store->datasheet))
143 gtk_tooltip_set_text (tooltip, _("Enter a number to add a new case."));
147 width = var_get_width (var);
149 value_init (&v, width);
150 datasheet_get_value (data_store->datasheet, row, var_get_case_index (var),
153 label = var_lookup_value_label (var, &v);
156 if (data_sheet->show_value_labels)
158 char *s = value_to_text (v, var);
159 gtk_tooltip_set_text (tooltip, s);
163 gtk_tooltip_set_text (tooltip, label);
165 value_destroy (&v, width);
167 return label != NULL;
171 render_row_number_cell (PsppSheetViewColumn *tree_column,
172 GtkCellRenderer *cell,
177 PsppireDataStore *store = store_;
178 GValue gvalue = { 0, };
181 row = GPOINTER_TO_INT (iter->user_data);
183 g_value_init (&gvalue, G_TYPE_INT);
184 g_value_set_int (&gvalue, row + 1);
185 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
186 g_value_unset (&gvalue);
188 if (row < datasheet_get_n_rows (store->datasheet))
189 g_object_set (cell, "editable", TRUE, NULL);
191 g_object_set (cell, "editable", FALSE, NULL);
194 "slash", psppire_data_store_filtered (store, row),
199 on_row_number_clicked (PsppireCellRendererButton *button,
201 PsppSheetView *sheet_view)
203 PsppSheetSelection *selection;
206 path = gtk_tree_path_new_from_string (path_string);
208 selection = pspp_sheet_view_get_selection (sheet_view);
209 pspp_sheet_selection_unselect_all (selection);
210 pspp_sheet_selection_select_path (selection, path);
211 pspp_sheet_selection_select_all_columns (selection);
213 gtk_tree_path_free (path);
217 make_row_number_column (PsppireDataSheet *data_sheet,
218 PsppireDataStore *ds)
220 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
221 PsppSheetViewColumn *column;
222 GtkCellRenderer *renderer;
224 renderer = psppire_cell_renderer_button_new ();
225 g_object_set (renderer, "xalign", 1.0, NULL);
226 g_signal_connect (renderer, "clicked", G_CALLBACK (on_row_number_clicked),
229 column = pspp_sheet_view_column_new_with_attributes (_("Case"),
231 pspp_sheet_view_column_set_selectable (column, TRUE);
232 pspp_sheet_view_column_set_row_head (column, TRUE);
233 pspp_sheet_view_column_set_tabbable (column, FALSE);
234 pspp_sheet_view_column_set_clickable (column, TRUE);
235 pspp_sheet_view_column_set_cell_data_func (
236 column, renderer, render_row_number_cell, ds, NULL);
237 pspp_sheet_view_column_set_fixed_width (column, 50);
238 pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers);
239 pspp_sheet_view_append_column (sheet_view, column);
243 render_data_cell (PsppSheetViewColumn *tree_column,
244 GtkCellRenderer *cell,
247 gpointer data_sheet_)
249 PsppireDataSheet *data_sheet = data_sheet_;
250 PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet);
251 struct variable *var;
257 row = GPOINTER_TO_INT (iter->user_data);
258 var = g_object_get_data (G_OBJECT (tree_column), "variable");
260 string = psppire_data_store_get_string (store, row, var,
261 data_sheet->show_value_labels);
264 GValue gvalue = { 0 };
266 g_value_init (&gvalue, G_TYPE_STRING);
267 g_value_take_string (&gvalue, string);
268 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
269 g_value_unset (&gvalue);
272 g_object_set (G_OBJECT (cell), "text", "", NULL);
274 switch (var_get_alignment (var))
276 case ALIGN_LEFT: xalign = 0.0; break;
277 case ALIGN_RIGHT: xalign = 1.0; break;
278 case ALIGN_CENTRE: xalign = 0.5; break;
279 default: xalign = 0.0; break;
288 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
292 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
293 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
294 NULL, NULL, NULL, &width, NULL);
299 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
306 ds_put_byte_multiple (&s, '0', char_cnt);
307 ds_put_byte (&s, ' ');
308 width = get_string_width (treeview, renderer, ds_cstr (&s));
315 on_data_column_editing_started (GtkCellRenderer *cell,
316 GtkCellEditable *editable,
320 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
321 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
322 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
323 struct variable *var;
325 g_return_if_fail (column);
326 g_return_if_fail (data_sheet);
327 g_return_if_fail (data_store);
330 g_object_ref (editable);
331 g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable",
332 editable, g_object_unref);
334 var = g_object_get_data (G_OBJECT (column), "variable");
335 g_return_if_fail (var);
337 if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable))
339 const struct val_labs *labels = var_get_value_labels (var);
340 const struct val_lab **vls = val_labs_sorted (labels);
341 size_t n_vls = val_labs_count (labels);
342 GtkListStore *list_store;
345 list_store = gtk_list_store_new (1, G_TYPE_STRING);
346 for (i = 0; i < n_vls; ++i)
348 const struct val_lab *vl = vls[i];
351 gtk_list_store_append (list_store, &iter);
352 gtk_list_store_set (list_store, &iter,
353 0, val_lab_get_label (vl),
358 gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
359 GTK_TREE_MODEL (list_store));
360 g_object_unref (list_store);
365 scroll_to_bottom (GtkWidget *widget,
366 GtkRequisition *requisition,
367 gpointer unused UNUSED)
369 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
370 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
371 GtkAdjustment *vadjust;
373 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
374 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
376 if (data_sheet->scroll_to_bottom_signal)
378 g_signal_handler_disconnect (data_sheet,
379 data_sheet->scroll_to_bottom_signal);
380 data_sheet->scroll_to_bottom_signal = 0;
385 on_data_column_edited (GtkCellRendererText *cell,
390 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
391 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
392 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
393 GtkEditable *editable;
394 struct variable *var;
400 path = gtk_tree_path_new_from_string (path_string);
401 row = gtk_tree_path_get_indices (path)[0];
402 gtk_tree_path_free (path);
404 var = g_object_get_data (G_OBJECT (column), "variable");
406 new_row = row == psppire_data_store_get_case_count (data_store);
407 if (new_row && new_text[0] == '\0')
410 editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
411 g_return_if_fail (editable != NULL);
412 is_val_lab = (GTK_IS_COMBO_BOX (editable)
413 && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
414 g_object_unref (editable);
416 psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
418 if (new_row && !data_sheet->scroll_to_bottom_signal)
420 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
421 data_sheet->scroll_to_bottom_signal =
422 g_signal_connect (data_sheet, "size-request",
423 G_CALLBACK (scroll_to_bottom), NULL);
427 /* We could be more specific about what to redraw, if it seems
428 important for performance. */
429 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
434 scroll_to_right (GtkWidget *widget,
435 PsppireDataSheet *data_sheet)
437 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
438 PsppSheetViewColumn *column, *prev;
439 GList *columns, *iter;
443 columns = pspp_sheet_view_get_columns (sheet_view);
444 for (iter = columns; iter; iter = iter->next)
446 PsppSheetViewColumn *c = iter->data;
447 if (g_object_get_data (G_OBJECT (c), "new-var-column"))
454 g_list_free (columns);
459 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
465 pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
468 pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
469 gtk_tree_path_free (path);
473 if (data_sheet->scroll_to_right_signal)
475 g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
476 data_sheet->scroll_to_right_signal = 0;
481 on_new_variable_column_edited (GtkCellRendererText *cell,
486 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
487 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
488 PsppireDict *dict = data_store->dict;
489 struct variable *var;
494 if (new_text[0] == '\0')
496 /* User didn't enter anything so don't create a variable. */
500 path = gtk_tree_path_new_from_string (path_string);
501 row = gtk_tree_path_get_indices (path)[0];
502 gtk_tree_path_free (path);
504 if (!psppire_dict_generate_name (dict, name, sizeof name))
507 var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
509 g_return_if_fail (var != NULL);
511 psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
513 if (!data_sheet->scroll_to_right_signal)
515 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
516 data_sheet->scroll_to_right_signal =
517 g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
518 G_CALLBACK (scroll_to_right), data_sheet);
522 /* We could be more specific about what to redraw, if it seems
523 important for performance. */
524 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
529 calc_width_conversion (PsppireDataSheet *data_sheet,
530 gint *base_width, gint *incr_width)
532 GtkCellRenderer *cell;
535 cell = gtk_cell_renderer_text_new ();
536 w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
537 w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
538 *incr_width = MAX (1, (w10 - w1) / 9);
539 *base_width = MAX (0, w10 - *incr_width * 10);
540 g_object_ref_sink (cell);
541 g_object_unref (cell);
545 display_width_from_pixel_width (PsppireDataSheet *data_sheet,
548 gint base_width, incr_width;
550 calc_width_conversion (data_sheet, &base_width, &incr_width);
551 return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
555 display_width_to_pixel_width (PsppireDataSheet *data_sheet,
560 return base_width + incr_width * display_width;
564 on_data_column_resized (GObject *gobject,
568 PsppireDataSheet *data_sheet = user_data;
569 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
570 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
571 struct variable *var;
575 if (data_store == NULL)
578 pixel_width = pspp_sheet_view_column_get_width (column);
579 if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
581 /* Short-circuit the expensive display_width_from_pixel_width()
582 calculation, to make loading .sav files with 2000 columns visibly
587 var = g_object_get_data (G_OBJECT (column), "variable");
588 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
589 var_set_display_width (var, display_width);
593 do_data_column_popup_menu (PsppSheetViewColumn *column,
594 guint button, guint32 time)
596 GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
597 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
600 menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
601 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
605 on_data_column_popup_menu (PsppSheetViewColumn *column,
606 gpointer user_data UNUSED)
608 do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
612 on_column_button_press_event (PsppSheetViewColumn *column,
613 GdkEventButton *event,
614 gpointer user_data UNUSED)
616 PsppSheetSelection *selection;
617 PsppSheetView *sheet_view;
619 sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
621 g_return_val_if_fail (sheet_view != NULL, FALSE);
623 selection = pspp_sheet_view_get_selection (sheet_view);
624 g_return_val_if_fail (selection != NULL, FALSE);
626 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
628 do_data_column_popup_menu (column, event->button, event->time);
631 else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
633 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
634 struct variable *var;
636 var = g_object_get_data (G_OBJECT (column), "variable");
641 g_signal_emit_by_name (data_sheet, "var-double-clicked",
642 var_get_dict_index (var), &handled);
651 on_data_column_query_tooltip (PsppSheetViewColumn *column,
653 gpointer user_data UNUSED)
655 struct variable *var;
658 var = g_object_get_data (G_OBJECT (column), "variable");
659 g_return_val_if_fail (var != NULL, FALSE);
661 text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
662 gtk_tooltip_set_text (tooltip, text);
668 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
669 PsppSheetViewColumn *column)
671 GtkCellRenderer *cell;
672 struct variable *var;
674 var = g_object_get_data (G_OBJECT (column), "variable");
675 g_return_if_fail (var != NULL);
677 if (var_has_value_labels (var))
679 cell = gtk_cell_renderer_combo_new ();
680 g_object_set (G_OBJECT (cell),
686 cell = gtk_cell_renderer_text_new ();
688 g_signal_connect (cell, "editing-started",
689 G_CALLBACK (on_data_column_editing_started), NULL);
690 g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
692 g_object_set_data (G_OBJECT (cell), "column", column);
693 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
695 pspp_sheet_view_column_clear (column);
696 pspp_sheet_view_column_pack_start (column, cell, TRUE);
698 pspp_sheet_view_column_set_cell_data_func (
699 column, cell, render_data_cell, data_sheet, NULL);
702 static PsppSheetViewColumn *
703 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
704 gint base_width, gint incr_width)
706 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
707 struct variable *var;
708 PsppSheetViewColumn *column;
712 var = psppire_dict_get_variable (data_store->dict, dict_idx);
714 column = pspp_sheet_view_column_new ();
716 name = escape_underscores (var_get_name (var));
717 pspp_sheet_view_column_set_title (column, name);
720 g_object_set_data (G_OBJECT (column), "variable", var);
722 width = display_width_to_pixel_width (data_sheet,
723 var_get_display_width (var),
724 base_width, incr_width);
725 pspp_sheet_view_column_set_min_width (column, 10);
726 pspp_sheet_view_column_set_fixed_width (column, width);
727 pspp_sheet_view_column_set_resizable (column, TRUE);
729 pspp_sheet_view_column_set_clickable (column, TRUE);
730 g_signal_connect (column, "notify::width",
731 G_CALLBACK (on_data_column_resized), data_sheet);
733 g_signal_connect (column, "button-press-event",
734 G_CALLBACK (on_column_button_press_event),
736 g_signal_connect (column, "query-tooltip",
737 G_CALLBACK (on_data_column_query_tooltip), NULL);
738 g_signal_connect (column, "popup-menu",
739 G_CALLBACK (on_data_column_popup_menu), data_sheet);
741 add_data_column_cell_renderer (data_sheet, column);
747 make_new_variable_column (PsppireDataSheet *data_sheet,
748 gint base_width, gint incr_width)
750 PsppSheetViewColumn *column;
751 GtkCellRenderer *cell;
754 cell = gtk_cell_renderer_text_new ();
755 g_object_set (cell, "editable", TRUE, NULL);
757 g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
760 column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
761 g_object_set_data (G_OBJECT (column), "new-var-column", column);
763 width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
764 pspp_sheet_view_column_set_min_width (column, 10);
765 pspp_sheet_view_column_set_fixed_width (column, width);
766 pspp_sheet_view_column_set_tabbable (column, FALSE);
768 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
769 g_signal_connect (column, "button-press-event",
770 G_CALLBACK (on_column_button_press_event),
772 g_signal_connect (column, "popup-menu",
773 G_CALLBACK (on_data_column_popup_menu), data_sheet);
775 pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
777 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
778 data_sheet->new_variable_column = column;
782 psppire_data_sheet_model_changed (GObject *gobject,
786 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
787 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
788 PsppireDataStore *data_store;
790 /* Remove old columns. */
793 PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
797 pspp_sheet_view_remove_column (sheet_view, column);
799 data_sheet->new_variable_column = NULL;
801 if (pspp_sheet_view_get_model (sheet_view) == NULL)
803 /* Don't create any columns at all if there's no model. Otherwise we'll
804 create some columns as part of the "dispose" callback for the sheet
805 view, which sets the model to NULL. That causes warnings to be
806 logged and is obviously undesirable in any case. */
810 /* Add new columns. */
811 data_store = psppire_data_sheet_get_data_store (data_sheet);
812 if (data_store != NULL)
814 gint base_width, incr_width;
817 calc_width_conversion (data_sheet, &base_width, &incr_width);
819 make_row_number_column (data_sheet, data_store);
820 for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
822 PsppSheetViewColumn *column;
824 column = make_data_column (data_sheet, i, base_width, incr_width);
825 pspp_sheet_view_append_column (sheet_view, column);
827 make_new_variable_column (data_sheet, base_width, incr_width);
838 PROP_MAY_CREATE_VARS,
839 PROP_MAY_DELETE_VARS,
844 psppire_data_sheet_set_property (GObject *object,
849 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
853 case PROP_DATA_STORE:
854 psppire_data_sheet_set_data_store (
855 obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
858 case PROP_VALUE_LABELS:
859 psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
862 case PROP_CASE_NUMBERS:
863 psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
866 case PROP_CURRENT_CASE:
867 psppire_data_sheet_goto_case (obj, g_value_get_long (value));
870 case PROP_MAY_CREATE_VARS:
871 psppire_data_sheet_set_may_create_vars (obj,
872 g_value_get_boolean (value));
875 case PROP_MAY_DELETE_VARS:
876 psppire_data_sheet_set_may_delete_vars (obj,
877 g_value_get_boolean (value));
880 case PROP_UI_MANAGER:
882 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
888 psppire_data_sheet_get_property (GObject *object,
893 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
897 case PROP_DATA_STORE:
898 g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
901 case PROP_VALUE_LABELS:
902 g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
905 case PROP_CASE_NUMBERS:
906 g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
909 case PROP_CURRENT_CASE:
910 g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
913 case PROP_MAY_CREATE_VARS:
914 g_value_set_boolean (value, obj->may_create_vars);
917 case PROP_MAY_DELETE_VARS:
918 g_value_set_boolean (value, obj->may_delete_vars);
921 case PROP_UI_MANAGER:
922 g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
926 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
932 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
934 return ds->show_value_labels;
938 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
939 gboolean show_value_labels)
941 show_value_labels = !!show_value_labels;
942 if (show_value_labels != ds->show_value_labels)
944 ds->show_value_labels = show_value_labels;
945 g_object_notify (G_OBJECT (ds), "value-labels");
946 gtk_widget_queue_draw (GTK_WIDGET (ds));
948 /* Make the cell being edited refresh too. */
949 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (ds), TRUE);
954 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
956 return ds->show_case_numbers;
960 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
961 gboolean show_case_numbers)
963 show_case_numbers = !!show_case_numbers;
964 if (show_case_numbers != ds->show_case_numbers)
966 PsppSheetViewColumn *column;
968 ds->show_case_numbers = show_case_numbers;
969 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
971 pspp_sheet_view_column_set_visible (column, show_case_numbers);
973 g_object_notify (G_OBJECT (ds), "case-numbers");
974 gtk_widget_queue_draw (GTK_WIDGET (ds));
979 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
981 return data_sheet->may_create_vars;
985 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
986 gboolean may_create_vars)
988 if (data_sheet->may_create_vars != may_create_vars)
990 data_sheet->may_create_vars = may_create_vars;
991 if (data_sheet->new_variable_column)
992 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
995 on_selection_changed (pspp_sheet_view_get_selection (
996 PSPP_SHEET_VIEW (data_sheet)), NULL);
1001 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
1003 return data_sheet->may_delete_vars;
1007 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
1008 gboolean may_delete_vars)
1010 if (data_sheet->may_delete_vars != may_delete_vars)
1012 data_sheet->may_delete_vars = may_delete_vars;
1013 on_selection_changed (pspp_sheet_view_get_selection (
1014 PSPP_SHEET_VIEW (data_sheet)), NULL);
1018 static PsppSheetViewColumn *
1019 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1022 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1023 PsppireDataStore *data_store;
1024 PsppSheetViewColumn *column;
1025 struct variable *var;
1028 data_store = psppire_data_sheet_get_data_store (data_sheet);
1029 g_return_val_if_fail (data_store != NULL, NULL);
1030 g_return_val_if_fail (data_store->dict != NULL, NULL);
1032 var = psppire_dict_get_variable (data_store->dict, dict_index);
1033 g_return_val_if_fail (var != NULL, NULL);
1036 list = pspp_sheet_view_get_columns (sheet_view);
1037 for (iter = list; iter != NULL; iter = iter->next)
1039 PsppSheetViewColumn *c = iter->data;
1042 v = g_object_get_data (G_OBJECT (c), "variable");
1055 psppire_data_sheet_goto_variable (PsppireDataSheet *data_sheet,
1058 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1059 PsppSheetViewColumn *column;
1061 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1067 gint row = psppire_data_sheet_get_current_case (data_sheet);
1068 path = gtk_tree_path_new_from_indices (row >= 0 ? row : 0, -1);
1070 pspp_sheet_view_scroll_to_cell (sheet_view, path, column,
1072 pspp_sheet_view_set_cursor (sheet_view, path, column, FALSE);
1073 gtk_tree_path_free (path);
1078 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1080 PsppSheetSelection *selection;
1081 struct variable *var;
1082 GList *selected_columns;
1085 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1086 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1089 for (iter = selected_columns; iter != NULL; iter = iter->next)
1091 PsppSheetViewColumn *column = iter->data;
1092 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1105 g_list_free (selected_columns);
1111 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1113 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1114 PsppireDataStore *store = data_sheet->data_store;
1115 PsppSheetSelection *selection;
1118 g_return_if_fail (case_index >= 0);
1119 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1121 path = gtk_tree_path_new_from_indices (case_index, -1);
1123 /* Select the case. */
1124 selection = pspp_sheet_view_get_selection (sheet_view);
1125 pspp_sheet_selection_unselect_all (selection);
1126 pspp_sheet_selection_select_path (selection, path);
1127 pspp_sheet_selection_select_all_columns (selection);
1129 /* Scroll so that the case is visible. */
1130 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1132 gtk_tree_path_free (path);
1135 /* Returns the 0-based index of a selected case, if there is at least one, and
1138 If more than one case is selected, returns the one with the smallest index,
1139 that is, the index of the case closest to the beginning of the file. The
1140 row that can be used to insert a new case is not considered a case. */
1142 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1144 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1145 PsppireDataStore *store = data_sheet->data_store;
1146 const struct range_set_node *node;
1147 PsppSheetSelection *selection;
1148 struct range_set *rows;
1151 selection = pspp_sheet_view_get_selection (sheet_view);
1152 rows = pspp_sheet_selection_get_range_set (selection);
1153 node = range_set_first (rows);
1154 row = (node && node->start < psppire_data_store_get_case_count (store)
1157 range_set_destroy (rows);
1162 /* Returns the 0-based index of a selected case, if exactly one case is
1163 selected, and -1 otherwise. Returns -1 if the row that can be used to
1164 insert a new case is selected. */
1166 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1168 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1169 PsppireDataStore *store = data_sheet->data_store;
1170 const struct range_set_node *node;
1171 PsppSheetSelection *selection;
1172 struct range_set *rows;
1175 selection = pspp_sheet_view_get_selection (sheet_view);
1176 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1179 rows = pspp_sheet_selection_get_range_set (selection);
1180 node = range_set_first (rows);
1181 row = (node && node->start < psppire_data_store_get_case_count (store)
1184 range_set_destroy (rows);
1190 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1192 if (data_sheet->uim == NULL)
1195 GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1197 GTK_TYPE_UI_MANAGER));
1198 g_object_ref (data_sheet->uim);
1201 return data_sheet->uim;
1205 psppire_data_sheet_dispose (GObject *object)
1207 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1209 if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0)
1211 g_signal_handler_disconnect (data_sheet->clip,
1212 data_sheet->on_owner_change_signal);
1213 data_sheet->on_owner_change_signal = 0;
1216 if (data_sheet->dispose_has_run)
1219 data_sheet->dispose_has_run = TRUE;
1221 psppire_data_sheet_unset_data_store (data_sheet);
1223 g_object_unref (data_sheet->builder);
1225 if (data_sheet->uim)
1226 g_object_unref (data_sheet->uim);
1228 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1232 psppire_data_sheet_map (GtkWidget *widget)
1234 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1236 GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
1238 data_sheet->clip = gtk_widget_get_clipboard (widget,
1239 GDK_SELECTION_CLIPBOARD);
1240 if (data_sheet->on_owner_change_signal)
1241 g_signal_handler_disconnect (data_sheet->clip,
1242 data_sheet->on_owner_change_signal);
1243 data_sheet->on_owner_change_signal
1244 = g_signal_connect (data_sheet->clip, "owner-change",
1245 G_CALLBACK (on_owner_change), widget);
1246 on_owner_change (data_sheet->clip, NULL, widget);
1250 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1252 GObjectClass *gobject_class;
1253 GtkWidgetClass *widget_class;
1255 gobject_class = G_OBJECT_CLASS (class);
1256 gobject_class->set_property = psppire_data_sheet_set_property;
1257 gobject_class->get_property = psppire_data_sheet_get_property;
1258 gobject_class->dispose = psppire_data_sheet_dispose;
1260 widget_class = GTK_WIDGET_CLASS (class);
1261 widget_class->map = psppire_data_sheet_map;
1263 g_signal_new ("var-double-clicked",
1264 G_OBJECT_CLASS_TYPE (gobject_class),
1267 g_signal_accumulator_true_handled, NULL,
1268 psppire_marshal_BOOLEAN__INT,
1269 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1271 g_object_class_install_property (
1272 gobject_class, PROP_DATA_STORE,
1273 g_param_spec_object ("data-store",
1275 "The data store for the data sheet to display.",
1276 PSPPIRE_TYPE_DATA_STORE,
1277 G_PARAM_WRITABLE | G_PARAM_READABLE));
1279 g_object_class_install_property (
1280 gobject_class, PROP_VALUE_LABELS,
1281 g_param_spec_boolean ("value-labels",
1283 "Whether or not the data sheet should display labels instead of values",
1285 G_PARAM_WRITABLE | G_PARAM_READABLE));
1287 g_object_class_install_property (
1288 gobject_class, PROP_CASE_NUMBERS,
1289 g_param_spec_boolean ("case-numbers",
1291 "Whether or not the data sheet should display case numbers",
1293 G_PARAM_WRITABLE | G_PARAM_READABLE));
1295 g_object_class_install_property (
1298 g_param_spec_long ("current-case",
1300 "Zero based number of the selected case",
1303 G_PARAM_WRITABLE | G_PARAM_READABLE));
1305 g_object_class_install_property (
1307 PROP_MAY_CREATE_VARS,
1308 g_param_spec_boolean ("may-create-vars",
1309 "May create variables",
1310 "Whether the user may create more variables",
1312 G_PARAM_READWRITE));
1314 g_object_class_install_property (
1316 PROP_MAY_DELETE_VARS,
1317 g_param_spec_boolean ("may-delete-vars",
1318 "May delete variables",
1319 "Whether the user may delete variables",
1321 G_PARAM_READWRITE));
1323 g_object_class_install_property (
1326 g_param_spec_object ("ui-manager",
1328 "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.",
1329 GTK_TYPE_UI_MANAGER,
1334 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1336 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1339 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1340 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1344 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1346 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1350 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1351 gpointer user_data UNUSED)
1353 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1355 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1357 PsppSheetSelection *selection;
1359 selection = pspp_sheet_view_get_selection (sheet_view);
1360 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1364 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1365 &path, NULL, NULL, NULL))
1367 pspp_sheet_selection_unselect_all (selection);
1368 pspp_sheet_selection_select_path (selection, path);
1369 pspp_sheet_selection_select_all_columns (selection);
1370 gtk_tree_path_free (path);
1374 do_popup_menu (widget, event->button, event->time);
1383 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1385 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1386 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1387 const struct range_set_node *node;
1388 struct range_set *selected;
1390 selected = pspp_sheet_selection_get_range_set (selection);
1391 for (node = range_set_last (selected); node != NULL;
1392 node = range_set_prev (selected, node))
1394 unsigned long int start = range_set_node_get_start (node);
1395 unsigned long int count = range_set_node_get_width (node);
1397 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1399 range_set_destroy (selected);
1403 on_selection_changed (PsppSheetSelection *selection,
1404 gpointer user_data UNUSED)
1406 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1407 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1408 gint n_selected_rows;
1409 gboolean any_variables_selected;
1410 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1415 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1417 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1418 gtk_action_set_sensitive (action, n_selected_rows > 0);
1420 switch (n_selected_rows)
1423 may_delete_cases = FALSE;
1427 /* The row used for inserting new cases cannot be deleted. */
1428 path = gtk_tree_path_new_from_indices (
1429 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1430 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1432 gtk_tree_path_free (path);
1436 may_delete_cases = TRUE;
1439 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1440 gtk_action_set_sensitive (action, may_delete_cases);
1442 any_variables_selected = FALSE;
1443 may_delete_vars = may_insert_vars = FALSE;
1444 list = pspp_sheet_selection_get_selected_columns (selection);
1445 for (iter = list; iter != NULL; iter = iter->next)
1447 PsppSheetViewColumn *column = iter->data;
1448 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1452 may_delete_vars = may_insert_vars = TRUE;
1453 any_variables_selected = TRUE;
1456 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1457 may_insert_vars = TRUE;
1461 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1462 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1464 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1465 gtk_action_set_sensitive (action, may_insert_vars);
1467 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1468 gtk_action_set_sensitive (action, may_delete_vars);
1470 action = get_action_assert (data_sheet->builder, "sort-up");
1471 gtk_action_set_sensitive (action, may_delete_vars);
1473 action = get_action_assert (data_sheet->builder, "sort-down");
1474 gtk_action_set_sensitive (action, may_delete_vars);
1476 psppire_data_sheet_update_clip_actions (data_sheet);
1477 psppire_data_sheet_update_primary_selection (data_sheet,
1478 (n_selected_rows > 0
1479 && any_variables_selected));
1483 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1484 struct range_set **rowsp,
1485 struct range_set **colsp)
1487 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1488 PsppireDataStore *data_store = data_sheet->data_store;
1489 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1490 unsigned long n_cases;
1491 struct range_set *rows, *cols;
1494 if (data_store == NULL)
1496 n_cases = psppire_data_store_get_case_count (data_store);
1498 rows = pspp_sheet_selection_get_range_set (selection);
1499 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1500 if (range_set_is_empty (rows))
1502 range_set_destroy (rows);
1506 cols = range_set_create ();
1507 list = pspp_sheet_selection_get_selected_columns (selection);
1508 for (iter = list; iter != NULL; iter = iter->next)
1510 PsppSheetViewColumn *column = iter->data;
1511 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1514 range_set_set1 (cols, var_get_dict_index (var), 1);
1517 if (range_set_is_empty (cols))
1519 range_set_destroy (rows);
1520 range_set_destroy (cols);
1530 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1532 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1533 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1534 PsppireDataStore *data_store = data_sheet->data_store;
1535 struct range_set *selected;
1538 selected = pspp_sheet_selection_get_range_set (selection);
1539 row = range_set_scan (selected, 0);
1540 range_set_destroy (selected);
1542 if (row <= psppire_data_store_get_case_count (data_store))
1543 psppire_data_store_insert_new_case (data_store, row);
1547 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1549 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1550 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1551 PsppireDict *dict = data_sheet->data_store->dict;
1552 PsppSheetViewColumn *column;
1553 struct variable *var;
1558 list = pspp_sheet_selection_get_selected_columns (selection);
1561 column = list->data;
1564 var = g_object_get_data (G_OBJECT (column), "variable");
1565 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1566 if (psppire_dict_generate_name (dict, name, sizeof name))
1567 psppire_dict_insert_variable (dict, index, name);
1571 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1573 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1574 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1575 PsppireDict *dict = data_sheet->data_store->dict;
1578 list = pspp_sheet_selection_get_selected_columns (selection);
1581 list = g_list_reverse (list);
1582 for (iter = list; iter; iter = iter->next)
1584 PsppSheetViewColumn *column = iter->data;
1585 struct variable *var;
1587 var = g_object_get_data (G_OBJECT (column), "variable");
1589 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1601 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1603 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1604 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1605 PsppireDataWindow *pdw;
1610 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1611 g_return_if_fail (pdw != NULL);
1613 list = pspp_sheet_selection_get_selected_columns (selection);
1615 syntax = g_string_new ("SORT CASES BY");
1617 for (iter = list; iter; iter = iter->next)
1619 PsppSheetViewColumn *column = iter->data;
1620 struct variable *var;
1622 var = g_object_get_data (G_OBJECT (column), "variable");
1625 g_string_append_printf (syntax, " %s", var_get_name (var));
1631 if (order == SORT_DESCEND)
1632 g_string_append (syntax, " (DOWN)");
1633 g_string_append_c (syntax, '.');
1634 execute_const_syntax_string (pdw, syntax->str);
1636 g_string_free (syntax, TRUE);
1640 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1642 do_sort (data_sheet, SORT_ASCEND);
1646 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1648 do_sort (data_sheet, SORT_DESCEND);
1652 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1654 goto_case_dialog (data_sheet);
1658 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1660 PsppireDataWindow *pdw;
1662 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1663 g_return_if_fail (pdw != NULL);
1669 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1671 psppire_data_sheet_set_clip (data_sheet, FALSE);
1675 on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet)
1677 psppire_data_sheet_set_clip (data_sheet, TRUE);
1681 on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet)
1683 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
1684 GtkClipboard *clipboard =
1685 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1687 gtk_clipboard_request_contents (clipboard,
1688 gdk_atom_intern ("UTF8_STRING", TRUE),
1689 psppire_data_sheet_clip_received_cb,
1694 psppire_data_sheet_init (PsppireDataSheet *obj)
1696 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1699 obj->show_value_labels = FALSE;
1700 obj->show_case_numbers = TRUE;
1701 obj->may_create_vars = TRUE;
1702 obj->may_delete_vars = TRUE;
1704 obj->owns_primary_selection = FALSE;
1706 obj->scroll_to_bottom_signal = 0;
1707 obj->scroll_to_right_signal = 0;
1708 obj->on_owner_change_signal = 0;
1709 obj->new_variable_column = NULL;
1710 obj->container = NULL;
1713 obj->dispose_has_run = FALSE;
1715 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1717 g_signal_connect (obj, "notify::model",
1718 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1720 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1721 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1722 PSPP_SHEET_SELECTION_RECTANGLE);
1724 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1725 g_signal_connect (obj, "query-tooltip",
1726 G_CALLBACK (on_query_tooltip), NULL);
1727 g_signal_connect (obj, "button-press-event",
1728 G_CALLBACK (on_button_pressed), NULL);
1729 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1731 obj->builder = builder_new ("data-sheet.ui");
1733 action = get_action_assert (obj->builder, "edit_clear-cases");
1734 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1736 gtk_action_set_sensitive (action, FALSE);
1737 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1738 "changed", G_CALLBACK (on_selection_changed), NULL);
1740 action = get_action_assert (obj->builder, "edit_insert-case");
1741 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1744 action = get_action_assert (obj->builder, "edit_insert-variable");
1745 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1748 action = get_action_assert (obj->builder, "edit_goto-case");
1749 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1752 action = get_action_assert (obj->builder, "edit_copy");
1753 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1755 action = get_action_assert (obj->builder, "edit_cut");
1756 g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj);
1758 action = get_action_assert (obj->builder, "edit_paste");
1759 g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj);
1761 action = get_action_assert (obj->builder, "edit_clear-variables");
1762 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1765 action = get_action_assert (obj->builder, "edit_find");
1766 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1768 action = get_action_assert (obj->builder, "sort-up");
1769 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1771 action = get_action_assert (obj->builder, "sort-down");
1772 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1777 psppire_data_sheet_new (void)
1779 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1783 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1785 return data_sheet->data_store;
1789 refresh_model (PsppireDataSheet *data_sheet)
1791 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1793 if (data_sheet->data_store != NULL)
1795 PsppireEmptyListStore *model;
1799 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1800 model = psppire_empty_list_store_new (n_rows);
1801 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1802 GTK_TREE_MODEL (model));
1803 g_object_unref (model);
1805 action = get_action_assert (data_sheet->builder, "edit_copy");
1806 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1812 on_case_inserted (PsppireDataStore *data_store, gint row,
1813 PsppireDataSheet *data_sheet)
1815 PsppireEmptyListStore *empty_list_store;
1816 GtkTreeModel *tree_model;
1819 g_return_if_fail (data_store == data_sheet->data_store);
1821 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1822 if (row == n_rows - 1)
1825 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1826 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1827 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1828 psppire_empty_list_store_row_inserted (empty_list_store, row);
1832 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1833 PsppireDataSheet *data_sheet)
1836 g_return_if_fail (data_store == data_sheet->data_store);
1840 /* This is a bit of a cop-out. We could do better, if it ever turns out
1841 that this performs too poorly. */
1842 refresh_model (data_sheet);
1846 PsppireEmptyListStore *empty_list_store;
1847 GtkTreeModel *tree_model;
1848 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1850 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1851 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1852 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1853 psppire_empty_list_store_row_deleted (empty_list_store, first);
1858 on_case_change (PsppireDataStore *data_store, gint row,
1859 PsppireDataSheet *data_sheet)
1861 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1863 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1864 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1868 on_backend_changed (PsppireDataStore *data_store,
1869 PsppireDataSheet *data_sheet)
1871 g_return_if_fail (data_store == data_sheet->data_store);
1872 refresh_model (data_sheet);
1876 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1877 PsppireDataSheet *data_sheet)
1879 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1880 PsppSheetViewColumn *column;
1881 struct variable *var;
1885 g_return_if_fail (data_sheet->data_store != NULL);
1886 g_return_if_fail (dict == data_sheet->data_store->dict);
1888 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1893 var = psppire_dict_get_variable (data_store->dict, dict_index);
1894 g_return_if_fail (var != NULL);
1896 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1897 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1898 if (display_width != var_get_display_width (var))
1900 gint base_width, incr_width;
1902 display_width = var_get_display_width (var);
1903 calc_width_conversion (data_sheet, &base_width, &incr_width);
1904 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1905 base_width, incr_width);
1906 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1911 on_variable_changed (PsppireDict *dict, int dict_index,
1912 guint what, const struct variable *oldvar,
1913 PsppireDataSheet *data_sheet)
1915 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1916 PsppSheetViewColumn *column;
1917 GtkCellRenderer *cell;
1918 struct variable *var;
1922 g_return_if_fail (data_sheet->data_store != NULL);
1923 g_return_if_fail (dict == data_sheet->data_store->dict);
1926 if (what & VAR_TRAIT_DISPLAY_WIDTH)
1927 on_variable_display_width_changed (dict, dict_index, data_sheet);
1929 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1935 var = psppire_dict_get_variable (data_store->dict, dict_index);
1936 g_return_if_fail (var != NULL);
1938 name = escape_underscores (var_get_name (var));
1939 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1940 pspp_sheet_view_column_set_title (column, name);
1943 cells = pspp_sheet_view_column_get_cell_renderers (column);
1944 g_return_if_fail (cells);
1946 g_list_free (cells);
1948 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1950 /* Stop editing before we delete and replace the cell renderers.
1951 Otherwise if this column is currently being edited, an eventual call
1952 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1953 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1955 It's possible that this is a bug in PsppSheetView, and it's possible
1956 that PsppSheetView inherits that from GtkTreeView, but I haven't
1957 investigated yet. */
1958 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1960 add_data_column_cell_renderer (data_sheet, column);
1965 on_variable_inserted (PsppireDict *dict, int var_index,
1966 PsppireDataSheet *data_sheet)
1968 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1969 gint base_width, incr_width;
1970 PsppSheetViewColumn *column;
1972 calc_width_conversion (data_sheet, &base_width, &incr_width);
1973 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1974 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1978 on_variable_deleted (PsppireDict *dict,
1979 const struct variable *var, int case_idx, int width,
1980 PsppireDataSheet *data_sheet)
1982 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1983 GList *columns, *iter;
1985 columns = pspp_sheet_view_get_columns (sheet_view);
1986 for (iter = columns; iter != NULL; iter = iter->next)
1988 PsppSheetViewColumn *column = iter->data;
1989 const struct variable *column_var;
1991 column_var = g_object_get_data (G_OBJECT (column), "variable");
1992 if (column_var == var)
1993 pspp_sheet_view_remove_column (sheet_view, column);
1995 g_list_free (columns);
1999 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
2001 PsppireDataStore *store = data_sheet->data_store;
2006 data_sheet->data_store = NULL;
2008 g_signal_handlers_disconnect_by_func (
2009 store, G_CALLBACK (on_backend_changed), data_sheet);
2010 g_signal_handlers_disconnect_by_func (
2011 store, G_CALLBACK (on_case_inserted), data_sheet);
2012 g_signal_handlers_disconnect_by_func (
2013 store, G_CALLBACK (on_cases_deleted), data_sheet);
2014 g_signal_handlers_disconnect_by_func (
2015 store, G_CALLBACK (on_case_change), data_sheet);
2017 g_signal_handlers_disconnect_by_func (
2018 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
2019 g_signal_handlers_disconnect_by_func (
2020 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
2021 g_signal_handlers_disconnect_by_func (
2022 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
2023 g_signal_handlers_disconnect_by_func (
2024 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
2026 g_object_unref (store);
2030 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
2031 PsppireDataStore *data_store)
2033 psppire_data_sheet_unset_data_store (data_sheet);
2035 data_sheet->data_store = data_store;
2036 if (data_store != NULL)
2038 g_object_ref (data_store);
2039 g_signal_connect (data_store, "backend-changed",
2040 G_CALLBACK (on_backend_changed), data_sheet);
2041 g_signal_connect (data_store, "case-inserted",
2042 G_CALLBACK (on_case_inserted), data_sheet);
2043 g_signal_connect (data_store, "cases-deleted",
2044 G_CALLBACK (on_cases_deleted), data_sheet);
2045 g_signal_connect (data_store, "case-changed",
2046 G_CALLBACK (on_case_change), data_sheet);
2048 /* XXX it's unclean to hook into the dict this way--what if the dict
2049 changes? As of this writing, though, nothing ever changes the
2050 data_store's dict. */
2051 g_signal_connect (data_store->dict, "variable-changed",
2052 G_CALLBACK (on_variable_changed),
2054 g_signal_connect (data_store->dict, "variable-inserted",
2055 G_CALLBACK (on_variable_inserted), data_sheet);
2056 g_signal_connect (data_store->dict, "variable-deleted",
2057 G_CALLBACK (on_variable_deleted), data_sheet);
2059 refresh_model (data_sheet);
2062 /* Clipboard stuff */
2064 /* A casereader and dictionary holding the data currently in the clip */
2065 static struct casereader *clip_datasheet = NULL;
2066 static struct dictionary *clip_dict = NULL;
2069 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2072 psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
2073 struct casereader **readerp,
2074 struct dictionary **dictp)
2076 struct casewriter *writer ;
2077 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2078 struct case_map *map = NULL;
2079 struct range_set *rows, *cols;
2080 const struct range_set_node *node;
2081 struct dictionary *dict;
2083 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2090 /* Construct clip dictionary. */
2091 *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
2092 RANGE_SET_FOR_EACH (node, cols)
2096 for (dict_index = node->start; dict_index < node->end; dict_index++)
2098 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2099 dict_clone_var_assert (dict, var);
2103 /* Construct clip data. */
2104 map = case_map_by_name (ds->dict->dict, dict);
2105 writer = autopaging_writer_create (dict_get_proto (dict));
2106 RANGE_SET_FOR_EACH (node, rows)
2108 unsigned long int row;
2110 for (row = node->start; row < node->end; row++)
2112 struct ccase *old = psppire_data_store_get_case (ds, row);
2114 casewriter_write (writer, case_map_execute (map, old));
2116 casewriter_force_error (writer);
2119 case_map_destroy (map);
2121 /* Clear data that we copied out, if we're doing a "cut". */
2122 if (cut && !casewriter_error (writer))
2124 RANGE_SET_FOR_EACH (node, rows)
2126 unsigned long int row;
2128 for (row = node->start; row < node->end; row++)
2130 const struct range_set_node *node2;
2132 RANGE_SET_FOR_EACH (node2, cols)
2136 for (dict_index = node2->start; dict_index < node2->end;
2139 struct variable *var;
2141 var = dict_get_var (ds->dict->dict, dict_index);
2142 psppire_data_store_set_string (ds, "", row,
2150 range_set_destroy (rows);
2151 range_set_destroy (cols);
2153 *readerp = casewriter_make_reader (writer);
2158 /* Set the clip from the currently selected range in DATA_SHEET. If CUT is
2159 true, clears the original data from DATA_SHEET, otherwise leaves the
2160 original data in-place. */
2162 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2165 struct casereader *reader;
2166 struct dictionary *dict;
2168 if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
2170 casereader_destroy (clip_datasheet);
2171 dict_destroy (clip_dict);
2173 clip_datasheet = reader;
2176 psppire_data_sheet_update_clipboard (data_sheet);
2187 /* Perform data_out for case CC, variable V, appending to STRING */
2189 data_out_g_string (GString *string, const struct variable *v,
2190 const struct ccase *cc)
2192 const struct fmt_spec *fs = var_get_print_format (v);
2193 const union value *val = case_data (cc, v);
2195 char *s = data_out (val, var_get_encoding (v), fs);
2197 g_string_append (string, s);
2203 clip_to_text (struct casereader *datasheet, struct dictionary *dict)
2208 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2209 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2210 const size_t var_cnt = dict_get_var_cnt (dict);
2212 string = g_string_sized_new (10 * val_cnt * case_cnt);
2214 for (r = 0 ; r < case_cnt ; ++r )
2219 cc = casereader_peek (datasheet, r);
2222 g_warning ("Clipboard seems to have inexplicably shrunk");
2226 for (c = 0 ; c < var_cnt ; ++c)
2228 const struct variable *v = dict_get_var (dict, c);
2229 data_out_g_string (string, v, cc);
2230 if ( c < val_cnt - 1 )
2231 g_string_append (string, "\t");
2235 g_string_append (string, "\n");
2245 clip_to_html (struct casereader *datasheet, struct dictionary *dict)
2250 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2251 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2252 const size_t var_cnt = dict_get_var_cnt (dict);
2254 /* Guestimate the size needed */
2255 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2257 g_string_append (string,
2258 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2260 g_string_append (string, "<table>\n");
2261 for (r = 0 ; r < case_cnt ; ++r )
2264 struct ccase *cc = casereader_peek (datasheet, r);
2267 g_warning ("Clipboard seems to have inexplicably shrunk");
2270 g_string_append (string, "<tr>\n");
2272 for (c = 0 ; c < var_cnt ; ++c)
2274 const struct variable *v = dict_get_var (dict, c);
2275 g_string_append (string, "<td>");
2276 data_out_g_string (string, v, cc);
2277 g_string_append (string, "</td>\n");
2280 g_string_append (string, "</tr>\n");
2284 g_string_append (string, "</table>\n");
2292 psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
2294 struct casereader *reader,
2295 struct dictionary *dict)
2297 GString *string = NULL;
2301 case SELECT_FMT_TEXT:
2302 string = clip_to_text (reader, dict);
2304 case SELECT_FMT_HTML:
2305 string = clip_to_html (reader, dict);
2308 g_assert_not_reached ();
2311 gtk_selection_data_set (selection_data, selection_data->target,
2313 (const guchar *) string->str, string->len);
2315 g_string_free (string, TRUE);
2319 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2320 GtkSelectionData *selection_data,
2324 psppire_data_sheet_clipboard_set (selection_data, info,
2325 clip_datasheet, clip_dict);
2329 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2332 dict_destroy (clip_dict);
2335 casereader_destroy (clip_datasheet);
2336 clip_datasheet = NULL;
2340 static const GtkTargetEntry targets[] = {
2341 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2342 { "STRING", 0, SELECT_FMT_TEXT },
2343 { "TEXT", 0, SELECT_FMT_TEXT },
2344 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2345 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2346 { "text/plain", 0, SELECT_FMT_TEXT },
2347 { "text/html", 0, SELECT_FMT_HTML }
2353 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2355 GtkClipboard *clipboard =
2356 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2357 GDK_SELECTION_CLIPBOARD);
2359 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2360 G_N_ELEMENTS (targets),
2361 psppire_data_sheet_clipboard_get_cb,
2362 psppire_data_sheet_clipboard_clear_cb,
2364 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2368 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2370 struct range_set *rows, *cols;
2374 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2377 range_set_destroy (rows);
2378 range_set_destroy (cols);
2381 action = get_action_assert (data_sheet->builder, "edit_copy");
2382 gtk_action_set_sensitive (action, enable);
2384 action = get_action_assert (data_sheet->builder, "edit_cut");
2385 gtk_action_set_sensitive (action, enable);
2389 psppire_data_sheet_primary_get_cb (GtkClipboard *clipboard,
2390 GtkSelectionData *selection_data,
2394 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2395 struct casereader *reader;
2396 struct dictionary *dict;
2398 if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
2400 psppire_data_sheet_clipboard_set (selection_data, info,
2402 casereader_destroy (reader);
2403 dict_destroy (dict);
2408 psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
2409 gboolean should_own)
2411 GtkClipboard *clipboard;
2412 GdkDisplay *display;
2414 display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
2415 clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
2416 g_return_if_fail (clipboard != NULL);
2418 if (data_sheet->owns_primary_selection && !should_own)
2420 data_sheet->owns_primary_selection = FALSE;
2421 gtk_clipboard_clear (clipboard);
2423 else if (should_own)
2424 data_sheet->owns_primary_selection =
2425 gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2426 psppire_data_sheet_primary_get_cb,
2427 NULL, G_OBJECT (data_sheet));
2430 /* A callback for when the clipboard contents have been received. */
2432 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2433 GtkSelectionData *sd,
2436 PsppireDataSheet *data_sheet = data;
2437 PsppireDataStore *store = data_sheet->data_store;
2438 struct range_set *rows, *cols;
2440 gint next_row, next_column;
2444 if ( sd->length < 0 )
2447 if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
2450 c = (char *) sd->data;
2452 /* Get the starting selected position in the data sheet. (Possibly we should
2453 only paste into the selected range if it's larger than one cell?) */
2454 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2456 next_row = range_set_first (rows)->start;
2457 first_column = next_column = range_set_first (cols)->start;
2458 range_set_destroy (rows);
2459 range_set_destroy (cols);
2461 g_return_if_fail (next_row >= 0);
2462 g_return_if_fail (next_column >= 0);
2464 while (count < sd->length)
2466 gint row = next_row;
2467 gint column = next_column;
2468 struct variable *var;
2471 while (*c != '\t' && *c != '\n' && count < sd->length)
2479 next_column = column + 1;
2481 else if ( *c == '\n')
2484 next_column = first_column;
2489 var = psppire_dict_get_variable (store->dict, column);
2491 psppire_data_store_set_string (store, s, row, var, FALSE);
2496 psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
2501 GtkAction *action = GTK_ACTION (data);
2502 gboolean compatible_target;
2505 compatible_target = FALSE;
2506 for (i = 0; i < G_N_ELEMENTS (targets); i++)
2508 GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
2511 for (j = 0; j < n_atoms; j++)
2512 if (target == atoms[j])
2514 compatible_target = TRUE;
2519 gtk_action_set_sensitive (action, compatible_target);
2520 g_object_unref (action);
2524 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2526 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2527 GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste");
2529 g_object_ref (action);
2530 gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb,