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 PsppireDataSheet *data_sheet)
1914 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1915 PsppSheetViewColumn *column;
1916 GtkCellRenderer *cell;
1917 struct variable *var;
1921 g_return_if_fail (data_sheet->data_store != NULL);
1922 g_return_if_fail (dict == data_sheet->data_store->dict);
1924 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1929 var = psppire_dict_get_variable (data_store->dict, dict_index);
1930 g_return_if_fail (var != NULL);
1932 name = escape_underscores (var_get_name (var));
1933 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1934 pspp_sheet_view_column_set_title (column, name);
1937 cells = pspp_sheet_view_column_get_cell_renderers (column);
1938 g_return_if_fail (cells);
1940 g_list_free (cells);
1942 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1944 /* Stop editing before we delete and replace the cell renderers.
1945 Otherwise if this column is currently being edited, an eventual call
1946 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1947 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1949 It's possible that this is a bug in PsppSheetView, and it's possible
1950 that PsppSheetView inherits that from GtkTreeView, but I haven't
1951 investigated yet. */
1952 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1954 add_data_column_cell_renderer (data_sheet, column);
1959 on_variable_inserted (PsppireDict *dict, int var_index,
1960 PsppireDataSheet *data_sheet)
1962 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1963 gint base_width, incr_width;
1964 PsppSheetViewColumn *column;
1966 calc_width_conversion (data_sheet, &base_width, &incr_width);
1967 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1968 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1972 on_variable_deleted (PsppireDict *dict,
1973 const struct variable *var, int case_idx, int width,
1974 PsppireDataSheet *data_sheet)
1976 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1977 GList *columns, *iter;
1979 columns = pspp_sheet_view_get_columns (sheet_view);
1980 for (iter = columns; iter != NULL; iter = iter->next)
1982 PsppSheetViewColumn *column = iter->data;
1983 const struct variable *column_var;
1985 column_var = g_object_get_data (G_OBJECT (column), "variable");
1986 if (column_var == var)
1987 pspp_sheet_view_remove_column (sheet_view, column);
1989 g_list_free (columns);
1993 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1995 PsppireDataStore *store = data_sheet->data_store;
2000 data_sheet->data_store = NULL;
2002 g_signal_handlers_disconnect_by_func (
2003 store, G_CALLBACK (on_backend_changed), data_sheet);
2004 g_signal_handlers_disconnect_by_func (
2005 store, G_CALLBACK (on_case_inserted), data_sheet);
2006 g_signal_handlers_disconnect_by_func (
2007 store, G_CALLBACK (on_cases_deleted), data_sheet);
2008 g_signal_handlers_disconnect_by_func (
2009 store, G_CALLBACK (on_case_change), data_sheet);
2011 g_signal_handlers_disconnect_by_func (
2012 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
2013 g_signal_handlers_disconnect_by_func (
2014 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
2015 g_signal_handlers_disconnect_by_func (
2016 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
2017 g_signal_handlers_disconnect_by_func (
2018 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
2020 g_object_unref (store);
2024 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
2025 PsppireDataStore *data_store)
2027 psppire_data_sheet_unset_data_store (data_sheet);
2029 data_sheet->data_store = data_store;
2030 if (data_store != NULL)
2032 g_object_ref (data_store);
2033 g_signal_connect (data_store, "backend-changed",
2034 G_CALLBACK (on_backend_changed), data_sheet);
2035 g_signal_connect (data_store, "case-inserted",
2036 G_CALLBACK (on_case_inserted), data_sheet);
2037 g_signal_connect (data_store, "cases-deleted",
2038 G_CALLBACK (on_cases_deleted), data_sheet);
2039 g_signal_connect (data_store, "case-changed",
2040 G_CALLBACK (on_case_change), data_sheet);
2042 /* XXX it's unclean to hook into the dict this way--what if the dict
2043 changes? As of this writing, though, nothing ever changes the
2044 data_store's dict. */
2045 g_signal_connect (data_store->dict, "variable-changed",
2046 G_CALLBACK (on_variable_changed),
2048 g_signal_connect (data_store->dict, "variable-display-width-changed",
2049 G_CALLBACK (on_variable_display_width_changed),
2051 g_signal_connect (data_store->dict, "variable-inserted",
2052 G_CALLBACK (on_variable_inserted), data_sheet);
2053 g_signal_connect (data_store->dict, "variable-deleted",
2054 G_CALLBACK (on_variable_deleted), data_sheet);
2056 refresh_model (data_sheet);
2059 /* Clipboard stuff */
2061 /* A casereader and dictionary holding the data currently in the clip */
2062 static struct casereader *clip_datasheet = NULL;
2063 static struct dictionary *clip_dict = NULL;
2066 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2069 psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
2070 struct casereader **readerp,
2071 struct dictionary **dictp)
2073 struct casewriter *writer ;
2074 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2075 struct case_map *map = NULL;
2076 struct range_set *rows, *cols;
2077 const struct range_set_node *node;
2078 struct dictionary *dict;
2080 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2087 /* Construct clip dictionary. */
2088 *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
2089 RANGE_SET_FOR_EACH (node, cols)
2093 for (dict_index = node->start; dict_index < node->end; dict_index++)
2095 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2096 dict_clone_var_assert (dict, var);
2100 /* Construct clip data. */
2101 map = case_map_by_name (ds->dict->dict, dict);
2102 writer = autopaging_writer_create (dict_get_proto (dict));
2103 RANGE_SET_FOR_EACH (node, rows)
2105 unsigned long int row;
2107 for (row = node->start; row < node->end; row++)
2109 struct ccase *old = psppire_data_store_get_case (ds, row);
2111 casewriter_write (writer, case_map_execute (map, old));
2113 casewriter_force_error (writer);
2116 case_map_destroy (map);
2118 /* Clear data that we copied out, if we're doing a "cut". */
2119 if (cut && !casewriter_error (writer))
2121 RANGE_SET_FOR_EACH (node, rows)
2123 unsigned long int row;
2125 for (row = node->start; row < node->end; row++)
2127 const struct range_set_node *node2;
2129 RANGE_SET_FOR_EACH (node2, cols)
2133 for (dict_index = node2->start; dict_index < node2->end;
2136 struct variable *var;
2138 var = dict_get_var (ds->dict->dict, dict_index);
2139 psppire_data_store_set_string (ds, "", row,
2147 range_set_destroy (rows);
2148 range_set_destroy (cols);
2150 *readerp = casewriter_make_reader (writer);
2155 /* Set the clip from the currently selected range in DATA_SHEET. If CUT is
2156 true, clears the original data from DATA_SHEET, otherwise leaves the
2157 original data in-place. */
2159 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2162 struct casereader *reader;
2163 struct dictionary *dict;
2165 if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
2167 casereader_destroy (clip_datasheet);
2168 dict_destroy (clip_dict);
2170 clip_datasheet = reader;
2173 psppire_data_sheet_update_clipboard (data_sheet);
2184 /* Perform data_out for case CC, variable V, appending to STRING */
2186 data_out_g_string (GString *string, const struct variable *v,
2187 const struct ccase *cc)
2189 const struct fmt_spec *fs = var_get_print_format (v);
2190 const union value *val = case_data (cc, v);
2192 char *s = data_out (val, var_get_encoding (v), fs);
2194 g_string_append (string, s);
2200 clip_to_text (struct casereader *datasheet, struct dictionary *dict)
2205 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2206 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2207 const size_t var_cnt = dict_get_var_cnt (dict);
2209 string = g_string_sized_new (10 * val_cnt * case_cnt);
2211 for (r = 0 ; r < case_cnt ; ++r )
2216 cc = casereader_peek (datasheet, r);
2219 g_warning ("Clipboard seems to have inexplicably shrunk");
2223 for (c = 0 ; c < var_cnt ; ++c)
2225 const struct variable *v = dict_get_var (dict, c);
2226 data_out_g_string (string, v, cc);
2227 if ( c < val_cnt - 1 )
2228 g_string_append (string, "\t");
2232 g_string_append (string, "\n");
2242 clip_to_html (struct casereader *datasheet, struct dictionary *dict)
2247 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2248 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2249 const size_t var_cnt = dict_get_var_cnt (dict);
2251 /* Guestimate the size needed */
2252 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2254 g_string_append (string,
2255 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2257 g_string_append (string, "<table>\n");
2258 for (r = 0 ; r < case_cnt ; ++r )
2261 struct ccase *cc = casereader_peek (datasheet, r);
2264 g_warning ("Clipboard seems to have inexplicably shrunk");
2267 g_string_append (string, "<tr>\n");
2269 for (c = 0 ; c < var_cnt ; ++c)
2271 const struct variable *v = dict_get_var (dict, c);
2272 g_string_append (string, "<td>");
2273 data_out_g_string (string, v, cc);
2274 g_string_append (string, "</td>\n");
2277 g_string_append (string, "</tr>\n");
2281 g_string_append (string, "</table>\n");
2289 psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
2291 struct casereader *reader,
2292 struct dictionary *dict)
2294 GString *string = NULL;
2298 case SELECT_FMT_TEXT:
2299 string = clip_to_text (reader, dict);
2301 case SELECT_FMT_HTML:
2302 string = clip_to_html (reader, dict);
2305 g_assert_not_reached ();
2308 gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
2310 (const guchar *) string->str, string->len);
2312 g_string_free (string, TRUE);
2316 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2317 GtkSelectionData *selection_data,
2321 psppire_data_sheet_clipboard_set (selection_data, info,
2322 clip_datasheet, clip_dict);
2326 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2329 dict_destroy (clip_dict);
2332 casereader_destroy (clip_datasheet);
2333 clip_datasheet = NULL;
2337 static const GtkTargetEntry targets[] = {
2338 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2339 { "STRING", 0, SELECT_FMT_TEXT },
2340 { "TEXT", 0, SELECT_FMT_TEXT },
2341 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2342 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2343 { "text/plain", 0, SELECT_FMT_TEXT },
2344 { "text/html", 0, SELECT_FMT_HTML }
2350 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2352 GtkClipboard *clipboard =
2353 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2354 GDK_SELECTION_CLIPBOARD);
2356 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2357 G_N_ELEMENTS (targets),
2358 psppire_data_sheet_clipboard_get_cb,
2359 psppire_data_sheet_clipboard_clear_cb,
2361 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2365 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2367 struct range_set *rows, *cols;
2371 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2374 range_set_destroy (rows);
2375 range_set_destroy (cols);
2378 action = get_action_assert (data_sheet->builder, "edit_copy");
2379 gtk_action_set_sensitive (action, enable);
2381 action = get_action_assert (data_sheet->builder, "edit_cut");
2382 gtk_action_set_sensitive (action, enable);
2386 psppire_data_sheet_primary_get_cb (GtkClipboard *clipboard,
2387 GtkSelectionData *selection_data,
2391 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2392 struct casereader *reader;
2393 struct dictionary *dict;
2395 if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
2397 psppire_data_sheet_clipboard_set (selection_data, info,
2399 casereader_destroy (reader);
2400 dict_destroy (dict);
2405 psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
2406 gboolean should_own)
2408 GtkClipboard *clipboard;
2409 GdkDisplay *display;
2411 display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
2412 clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
2413 g_return_if_fail (clipboard != NULL);
2415 if (data_sheet->owns_primary_selection && !should_own)
2417 data_sheet->owns_primary_selection = FALSE;
2418 gtk_clipboard_clear (clipboard);
2420 else if (should_own)
2421 data_sheet->owns_primary_selection =
2422 gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2423 psppire_data_sheet_primary_get_cb,
2424 NULL, G_OBJECT (data_sheet));
2427 /* A callback for when the clipboard contents have been received. */
2429 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2430 GtkSelectionData *sd,
2433 PsppireDataSheet *data_sheet = data;
2434 PsppireDataStore *store = data_sheet->data_store;
2435 struct range_set *rows, *cols;
2437 gint next_row, next_column;
2441 if ( gtk_selection_data_get_length (sd) < 0 )
2444 if ( gtk_selection_data_get_data_type (sd) != gdk_atom_intern ("UTF8_STRING", FALSE))
2447 c = (char *) gtk_selection_data_get_data (sd);
2449 /* Get the starting selected position in the data sheet. (Possibly we should
2450 only paste into the selected range if it's larger than one cell?) */
2451 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2453 next_row = range_set_first (rows)->start;
2454 first_column = next_column = range_set_first (cols)->start;
2455 range_set_destroy (rows);
2456 range_set_destroy (cols);
2458 g_return_if_fail (next_row >= 0);
2459 g_return_if_fail (next_column >= 0);
2461 while (count < gtk_selection_data_get_length (sd))
2463 gint row = next_row;
2464 gint column = next_column;
2465 struct variable *var;
2468 while (*c != '\t' && *c != '\n' && count < gtk_selection_data_get_length (sd))
2476 next_column = column + 1;
2478 else if ( *c == '\n')
2481 next_column = first_column;
2486 var = psppire_dict_get_variable (store->dict, column);
2488 psppire_data_store_set_string (store, s, row, var, FALSE);
2493 psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
2498 GtkAction *action = GTK_ACTION (data);
2499 gboolean compatible_target;
2502 compatible_target = FALSE;
2503 for (i = 0; i < G_N_ELEMENTS (targets); i++)
2505 GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
2508 for (j = 0; j < n_atoms; j++)
2509 if (target == atoms[j])
2511 compatible_target = TRUE;
2516 gtk_action_set_sensitive (action, compatible_target);
2517 g_object_unref (action);
2521 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2523 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2524 GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste");
2526 g_object_ref (action);
2527 gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb,