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);
127 g_return_val_if_fail (data_store->datasheet != NULL, FALSE);
129 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
132 var = g_object_get_data (G_OBJECT (column), "variable");
135 if (g_object_get_data (G_OBJECT (column), "new-var-column") == NULL)
138 gtk_tooltip_set_text (tooltip,
139 _("Enter a number to add a new variable."));
142 else if (row >= datasheet_get_n_rows (data_store->datasheet))
144 gtk_tooltip_set_text (tooltip, _("Enter a number to add a new case."));
148 width = var_get_width (var);
150 value_init (&v, width);
151 datasheet_get_value (data_store->datasheet, row, var_get_case_index (var),
154 label = var_lookup_value_label (var, &v);
157 if (data_sheet->show_value_labels)
159 char *s = value_to_text (v, var);
160 gtk_tooltip_set_text (tooltip, s);
164 gtk_tooltip_set_text (tooltip, label);
166 value_destroy (&v, width);
168 return label != NULL;
172 render_row_number_cell (PsppSheetViewColumn *tree_column,
173 GtkCellRenderer *cell,
178 PsppireDataStore *store = store_;
179 GValue gvalue = { 0, };
180 gint row = GPOINTER_TO_INT (iter->user_data);
182 g_return_if_fail (store->datasheet);
184 g_value_init (&gvalue, G_TYPE_INT);
185 g_value_set_int (&gvalue, row + 1);
186 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
187 g_value_unset (&gvalue);
189 if (row < datasheet_get_n_rows (store->datasheet))
190 g_object_set (cell, "editable", TRUE, NULL);
192 g_object_set (cell, "editable", FALSE, NULL);
195 "slash", psppire_data_store_filtered (store, row),
200 on_row_number_clicked (PsppireCellRendererButton *button,
202 PsppSheetView *sheet_view)
204 PsppSheetSelection *selection;
207 path = gtk_tree_path_new_from_string (path_string);
209 selection = pspp_sheet_view_get_selection (sheet_view);
210 pspp_sheet_selection_unselect_all (selection);
211 pspp_sheet_selection_select_path (selection, path);
212 pspp_sheet_selection_select_all_columns (selection);
214 gtk_tree_path_free (path);
218 make_row_number_column (PsppireDataSheet *data_sheet,
219 PsppireDataStore *ds)
221 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
222 PsppSheetViewColumn *column;
223 GtkCellRenderer *renderer;
225 renderer = psppire_cell_renderer_button_new ();
226 g_object_set (renderer, "xalign", 1.0, NULL);
227 g_signal_connect (renderer, "clicked", G_CALLBACK (on_row_number_clicked),
230 column = pspp_sheet_view_column_new_with_attributes (_("Case"),
232 pspp_sheet_view_column_set_selectable (column, TRUE);
233 pspp_sheet_view_column_set_row_head (column, TRUE);
234 pspp_sheet_view_column_set_tabbable (column, FALSE);
235 pspp_sheet_view_column_set_clickable (column, TRUE);
236 pspp_sheet_view_column_set_cell_data_func (
237 column, renderer, render_row_number_cell, ds, NULL);
238 pspp_sheet_view_column_set_fixed_width (column, 50);
239 pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers);
240 pspp_sheet_view_append_column (sheet_view, column);
244 render_data_cell (PsppSheetViewColumn *tree_column,
245 GtkCellRenderer *cell,
248 gpointer data_sheet_)
250 PsppireDataSheet *data_sheet = data_sheet_;
251 PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet);
252 struct variable *var;
258 row = GPOINTER_TO_INT (iter->user_data);
259 var = g_object_get_data (G_OBJECT (tree_column), "variable");
261 string = psppire_data_store_get_string (store, row, var,
262 data_sheet->show_value_labels);
265 GValue gvalue = { 0 };
267 g_value_init (&gvalue, G_TYPE_STRING);
268 g_value_take_string (&gvalue, string);
269 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
270 g_value_unset (&gvalue);
273 g_object_set (G_OBJECT (cell), "text", "", NULL);
275 switch (var_get_alignment (var))
277 case ALIGN_LEFT: xalign = 0.0; break;
278 case ALIGN_RIGHT: xalign = 1.0; break;
279 case ALIGN_CENTRE: xalign = 0.5; break;
280 default: xalign = 0.0; break;
289 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
293 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
294 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
295 NULL, NULL, NULL, &width, NULL);
300 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
307 ds_put_byte_multiple (&s, '0', char_cnt);
308 ds_put_byte (&s, ' ');
309 width = get_string_width (treeview, renderer, ds_cstr (&s));
316 on_data_column_editing_started (GtkCellRenderer *cell,
317 GtkCellEditable *editable,
321 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
322 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
323 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
324 struct variable *var;
326 g_return_if_fail (column);
327 g_return_if_fail (data_sheet);
328 g_return_if_fail (data_store);
331 g_object_ref (editable);
332 g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable",
333 editable, g_object_unref);
335 var = g_object_get_data (G_OBJECT (column), "variable");
336 g_return_if_fail (var);
338 if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable))
340 const struct val_labs *labels = var_get_value_labels (var);
341 const struct val_lab **vls = val_labs_sorted (labels);
342 size_t n_vls = val_labs_count (labels);
343 GtkListStore *list_store;
346 list_store = gtk_list_store_new (1, G_TYPE_STRING);
347 for (i = 0; i < n_vls; ++i)
349 const struct val_lab *vl = vls[i];
352 gtk_list_store_append (list_store, &iter);
353 gtk_list_store_set (list_store, &iter,
354 0, val_lab_get_label (vl),
359 gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
360 GTK_TREE_MODEL (list_store));
361 g_object_unref (list_store);
366 scroll_to_bottom (GtkWidget *widget,
367 GtkRequisition *requisition,
368 gpointer unused UNUSED)
370 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
371 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
372 GtkAdjustment *vadjust;
374 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
375 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
377 if (data_sheet->scroll_to_bottom_signal)
379 g_signal_handler_disconnect (data_sheet,
380 data_sheet->scroll_to_bottom_signal);
381 data_sheet->scroll_to_bottom_signal = 0;
386 on_data_column_edited (GtkCellRendererText *cell,
391 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
392 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
393 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
394 GtkEditable *editable;
395 struct variable *var;
401 path = gtk_tree_path_new_from_string (path_string);
402 row = gtk_tree_path_get_indices (path)[0];
403 gtk_tree_path_free (path);
405 var = g_object_get_data (G_OBJECT (column), "variable");
407 new_row = row == psppire_data_store_get_case_count (data_store);
408 if (new_row && new_text[0] == '\0')
411 editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
412 g_return_if_fail (editable != NULL);
413 is_val_lab = (GTK_IS_COMBO_BOX (editable)
414 && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
415 g_object_unref (editable);
417 psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
419 if (new_row && !data_sheet->scroll_to_bottom_signal)
421 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
422 data_sheet->scroll_to_bottom_signal =
423 g_signal_connect (data_sheet, "size-request",
424 G_CALLBACK (scroll_to_bottom), NULL);
428 /* We could be more specific about what to redraw, if it seems
429 important for performance. */
430 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
435 scroll_to_right (GtkWidget *widget,
436 PsppireDataSheet *data_sheet)
438 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
439 PsppSheetViewColumn *column, *prev;
440 GList *columns, *iter;
444 columns = pspp_sheet_view_get_columns (sheet_view);
445 for (iter = columns; iter; iter = iter->next)
447 PsppSheetViewColumn *c = iter->data;
448 if (g_object_get_data (G_OBJECT (c), "new-var-column"))
455 g_list_free (columns);
460 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
466 pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
469 pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
470 gtk_tree_path_free (path);
474 if (data_sheet->scroll_to_right_signal)
476 g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
477 data_sheet->scroll_to_right_signal = 0;
482 on_new_variable_column_edited (GtkCellRendererText *cell,
487 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
488 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
489 PsppireDict *dict = data_store->dict;
490 struct variable *var;
495 if (new_text[0] == '\0')
497 /* User didn't enter anything so don't create a variable. */
501 path = gtk_tree_path_new_from_string (path_string);
502 row = gtk_tree_path_get_indices (path)[0];
503 gtk_tree_path_free (path);
505 if (!psppire_dict_generate_name (dict, name, sizeof name))
508 var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
510 g_return_if_fail (var != NULL);
512 psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
514 if (!data_sheet->scroll_to_right_signal)
516 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
517 data_sheet->scroll_to_right_signal =
518 g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
519 G_CALLBACK (scroll_to_right), data_sheet);
523 /* We could be more specific about what to redraw, if it seems
524 important for performance. */
525 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
530 calc_width_conversion (PsppireDataSheet *data_sheet,
531 gint *base_width, gint *incr_width)
533 GtkCellRenderer *cell;
536 cell = gtk_cell_renderer_text_new ();
537 w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
538 w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
539 *incr_width = MAX (1, (w10 - w1) / 9);
540 *base_width = MAX (0, w10 - *incr_width * 10);
541 g_object_ref_sink (cell);
542 g_object_unref (cell);
546 display_width_from_pixel_width (PsppireDataSheet *data_sheet,
549 gint base_width, incr_width;
551 calc_width_conversion (data_sheet, &base_width, &incr_width);
552 return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
556 display_width_to_pixel_width (PsppireDataSheet *data_sheet,
561 return base_width + incr_width * display_width;
565 on_data_column_resized (GObject *gobject,
569 PsppireDataSheet *data_sheet = user_data;
570 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
571 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
572 struct variable *var;
576 if (data_store == NULL)
579 pixel_width = pspp_sheet_view_column_get_width (column);
580 if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
582 /* Short-circuit the expensive display_width_from_pixel_width()
583 calculation, to make loading .sav files with 2000 columns visibly
588 var = g_object_get_data (G_OBJECT (column), "variable");
589 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
590 var_set_display_width (var, display_width);
594 do_data_column_popup_menu (PsppSheetViewColumn *column,
595 guint button, guint32 time)
597 GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
598 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
601 menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
602 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
606 on_data_column_popup_menu (PsppSheetViewColumn *column,
607 gpointer user_data UNUSED)
609 do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
613 on_column_button_press_event (PsppSheetViewColumn *column,
614 GdkEventButton *event,
615 gpointer user_data UNUSED)
617 PsppSheetSelection *selection;
618 PsppSheetView *sheet_view;
620 sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
622 g_return_val_if_fail (sheet_view != NULL, FALSE);
624 selection = pspp_sheet_view_get_selection (sheet_view);
625 g_return_val_if_fail (selection != NULL, FALSE);
627 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
629 do_data_column_popup_menu (column, event->button, event->time);
632 else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
634 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
635 struct variable *var;
637 var = g_object_get_data (G_OBJECT (column), "variable");
642 g_signal_emit_by_name (data_sheet, "var-double-clicked",
643 var_get_dict_index (var), &handled);
652 on_data_column_query_tooltip (PsppSheetViewColumn *column,
654 gpointer user_data UNUSED)
656 struct variable *var;
659 var = g_object_get_data (G_OBJECT (column), "variable");
660 g_return_val_if_fail (var != NULL, FALSE);
662 text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
663 gtk_tooltip_set_text (tooltip, text);
669 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
670 PsppSheetViewColumn *column)
672 GtkCellRenderer *cell;
673 struct variable *var;
675 var = g_object_get_data (G_OBJECT (column), "variable");
676 g_return_if_fail (var != NULL);
678 if (var_has_value_labels (var))
680 cell = gtk_cell_renderer_combo_new ();
681 g_object_set (G_OBJECT (cell),
687 cell = gtk_cell_renderer_text_new ();
689 g_signal_connect (cell, "editing-started",
690 G_CALLBACK (on_data_column_editing_started), NULL);
691 g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
693 g_object_set_data (G_OBJECT (cell), "column", column);
694 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
696 pspp_sheet_view_column_clear (column);
697 pspp_sheet_view_column_pack_start (column, cell, TRUE);
699 pspp_sheet_view_column_set_cell_data_func (
700 column, cell, render_data_cell, data_sheet, NULL);
703 static PsppSheetViewColumn *
704 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
705 gint base_width, gint incr_width)
707 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
708 struct variable *var;
709 PsppSheetViewColumn *column;
713 var = psppire_dict_get_variable (data_store->dict, dict_idx);
715 column = pspp_sheet_view_column_new ();
717 name = escape_underscores (var_get_name (var));
718 pspp_sheet_view_column_set_title (column, name);
721 g_object_set_data (G_OBJECT (column), "variable", var);
723 width = display_width_to_pixel_width (data_sheet,
724 var_get_display_width (var),
725 base_width, incr_width);
726 pspp_sheet_view_column_set_min_width (column, 10);
727 pspp_sheet_view_column_set_fixed_width (column, width);
728 pspp_sheet_view_column_set_resizable (column, TRUE);
730 pspp_sheet_view_column_set_clickable (column, TRUE);
731 g_signal_connect (column, "notify::width",
732 G_CALLBACK (on_data_column_resized), data_sheet);
734 g_signal_connect (column, "button-press-event",
735 G_CALLBACK (on_column_button_press_event),
737 g_signal_connect (column, "query-tooltip",
738 G_CALLBACK (on_data_column_query_tooltip), NULL);
739 g_signal_connect (column, "popup-menu",
740 G_CALLBACK (on_data_column_popup_menu), data_sheet);
742 add_data_column_cell_renderer (data_sheet, column);
748 make_new_variable_column (PsppireDataSheet *data_sheet,
749 gint base_width, gint incr_width)
751 PsppSheetViewColumn *column;
752 GtkCellRenderer *cell;
755 cell = gtk_cell_renderer_text_new ();
756 g_object_set (cell, "editable", TRUE, NULL);
758 g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
761 column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
762 g_object_set_data (G_OBJECT (column), "new-var-column", column);
764 width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
765 pspp_sheet_view_column_set_min_width (column, 10);
766 pspp_sheet_view_column_set_fixed_width (column, width);
767 pspp_sheet_view_column_set_tabbable (column, FALSE);
769 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
770 g_signal_connect (column, "button-press-event",
771 G_CALLBACK (on_column_button_press_event),
773 g_signal_connect (column, "popup-menu",
774 G_CALLBACK (on_data_column_popup_menu), data_sheet);
776 pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
778 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
779 data_sheet->new_variable_column = column;
783 psppire_data_sheet_model_changed (GObject *gobject,
787 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
788 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
789 PsppireDataStore *data_store;
791 /* Remove old columns. */
794 PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
798 pspp_sheet_view_remove_column (sheet_view, column);
800 data_sheet->new_variable_column = NULL;
802 if (pspp_sheet_view_get_model (sheet_view) == NULL)
804 /* Don't create any columns at all if there's no model. Otherwise we'll
805 create some columns as part of the "dispose" callback for the sheet
806 view, which sets the model to NULL. That causes warnings to be
807 logged and is obviously undesirable in any case. */
811 /* Add new columns. */
812 data_store = psppire_data_sheet_get_data_store (data_sheet);
813 if (data_store != NULL)
815 gint base_width, incr_width;
818 calc_width_conversion (data_sheet, &base_width, &incr_width);
820 make_row_number_column (data_sheet, data_store);
821 for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
823 PsppSheetViewColumn *column;
825 column = make_data_column (data_sheet, i, base_width, incr_width);
826 pspp_sheet_view_append_column (sheet_view, column);
828 make_new_variable_column (data_sheet, base_width, incr_width);
839 PROP_MAY_CREATE_VARS,
840 PROP_MAY_DELETE_VARS,
845 psppire_data_sheet_set_property (GObject *object,
850 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
854 case PROP_DATA_STORE:
855 psppire_data_sheet_set_data_store (
856 obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
859 case PROP_VALUE_LABELS:
860 psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
863 case PROP_CASE_NUMBERS:
864 psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
867 case PROP_CURRENT_CASE:
868 psppire_data_sheet_goto_case (obj, g_value_get_long (value));
871 case PROP_MAY_CREATE_VARS:
872 psppire_data_sheet_set_may_create_vars (obj,
873 g_value_get_boolean (value));
876 case PROP_MAY_DELETE_VARS:
877 psppire_data_sheet_set_may_delete_vars (obj,
878 g_value_get_boolean (value));
881 case PROP_UI_MANAGER:
883 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
889 psppire_data_sheet_get_property (GObject *object,
894 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
898 case PROP_DATA_STORE:
899 g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
902 case PROP_VALUE_LABELS:
903 g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
906 case PROP_CASE_NUMBERS:
907 g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
910 case PROP_CURRENT_CASE:
911 g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
914 case PROP_MAY_CREATE_VARS:
915 g_value_set_boolean (value, obj->may_create_vars);
918 case PROP_MAY_DELETE_VARS:
919 g_value_set_boolean (value, obj->may_delete_vars);
922 case PROP_UI_MANAGER:
923 g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
927 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
933 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
935 return ds->show_value_labels;
939 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
940 gboolean show_value_labels)
942 show_value_labels = !!show_value_labels;
943 if (show_value_labels != ds->show_value_labels)
945 ds->show_value_labels = show_value_labels;
946 g_object_notify (G_OBJECT (ds), "value-labels");
947 gtk_widget_queue_draw (GTK_WIDGET (ds));
949 /* Make the cell being edited refresh too. */
950 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (ds), TRUE);
955 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
957 return ds->show_case_numbers;
961 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
962 gboolean show_case_numbers)
964 show_case_numbers = !!show_case_numbers;
965 if (show_case_numbers != ds->show_case_numbers)
967 PsppSheetViewColumn *column;
969 ds->show_case_numbers = show_case_numbers;
970 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
972 pspp_sheet_view_column_set_visible (column, show_case_numbers);
974 g_object_notify (G_OBJECT (ds), "case-numbers");
975 gtk_widget_queue_draw (GTK_WIDGET (ds));
980 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
982 return data_sheet->may_create_vars;
986 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
987 gboolean may_create_vars)
989 if (data_sheet->may_create_vars != may_create_vars)
991 data_sheet->may_create_vars = may_create_vars;
992 if (data_sheet->new_variable_column)
993 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
996 on_selection_changed (pspp_sheet_view_get_selection (
997 PSPP_SHEET_VIEW (data_sheet)), NULL);
1002 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
1004 return data_sheet->may_delete_vars;
1008 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
1009 gboolean may_delete_vars)
1011 if (data_sheet->may_delete_vars != may_delete_vars)
1013 data_sheet->may_delete_vars = may_delete_vars;
1014 on_selection_changed (pspp_sheet_view_get_selection (
1015 PSPP_SHEET_VIEW (data_sheet)), NULL);
1019 static PsppSheetViewColumn *
1020 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1023 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1024 PsppireDataStore *data_store;
1025 PsppSheetViewColumn *column;
1026 struct variable *var;
1029 data_store = psppire_data_sheet_get_data_store (data_sheet);
1030 g_return_val_if_fail (data_store != NULL, NULL);
1031 g_return_val_if_fail (data_store->dict != NULL, NULL);
1033 var = psppire_dict_get_variable (data_store->dict, dict_index);
1034 g_return_val_if_fail (var != NULL, NULL);
1037 list = pspp_sheet_view_get_columns (sheet_view);
1038 for (iter = list; iter != NULL; iter = iter->next)
1040 PsppSheetViewColumn *c = iter->data;
1043 v = g_object_get_data (G_OBJECT (c), "variable");
1056 psppire_data_sheet_goto_variable (PsppireDataSheet *data_sheet,
1059 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1060 PsppSheetViewColumn *column;
1062 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1068 gint row = psppire_data_sheet_get_current_case (data_sheet);
1069 path = gtk_tree_path_new_from_indices (row >= 0 ? row : 0, -1);
1071 pspp_sheet_view_scroll_to_cell (sheet_view, path, column,
1073 pspp_sheet_view_set_cursor (sheet_view, path, column, FALSE);
1074 gtk_tree_path_free (path);
1079 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1081 PsppSheetSelection *selection;
1082 struct variable *var;
1083 GList *selected_columns;
1086 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1087 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1090 for (iter = selected_columns; iter != NULL; iter = iter->next)
1092 PsppSheetViewColumn *column = iter->data;
1093 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1106 g_list_free (selected_columns);
1112 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1114 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1115 PsppireDataStore *store = data_sheet->data_store;
1116 PsppSheetSelection *selection;
1119 g_return_if_fail (case_index >= 0);
1120 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1122 path = gtk_tree_path_new_from_indices (case_index, -1);
1124 /* Select the case. */
1125 selection = pspp_sheet_view_get_selection (sheet_view);
1126 pspp_sheet_selection_unselect_all (selection);
1127 pspp_sheet_selection_select_path (selection, path);
1128 pspp_sheet_selection_select_all_columns (selection);
1130 /* Scroll so that the case is visible. */
1131 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1133 gtk_tree_path_free (path);
1136 /* Returns the 0-based index of a selected case, if there is at least one, and
1139 If more than one case is selected, returns the one with the smallest index,
1140 that is, the index of the case closest to the beginning of the file. The
1141 row that can be used to insert a new case is not considered a case. */
1143 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1145 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1146 PsppireDataStore *store = data_sheet->data_store;
1147 const struct range_set_node *node;
1148 PsppSheetSelection *selection;
1149 struct range_set *rows;
1152 selection = pspp_sheet_view_get_selection (sheet_view);
1153 rows = pspp_sheet_selection_get_range_set (selection);
1154 node = range_set_first (rows);
1155 row = (node && node->start < psppire_data_store_get_case_count (store)
1158 range_set_destroy (rows);
1163 /* Returns the 0-based index of a selected case, if exactly one case is
1164 selected, and -1 otherwise. Returns -1 if the row that can be used to
1165 insert a new case is selected. */
1167 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1169 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1170 PsppireDataStore *store = data_sheet->data_store;
1171 const struct range_set_node *node;
1172 PsppSheetSelection *selection;
1173 struct range_set *rows;
1176 selection = pspp_sheet_view_get_selection (sheet_view);
1177 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1180 rows = pspp_sheet_selection_get_range_set (selection);
1181 node = range_set_first (rows);
1182 row = (node && node->start < psppire_data_store_get_case_count (store)
1185 range_set_destroy (rows);
1191 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1193 if (data_sheet->uim == NULL)
1196 GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1198 GTK_TYPE_UI_MANAGER));
1199 g_object_ref (data_sheet->uim);
1202 return data_sheet->uim;
1206 psppire_data_sheet_dispose (GObject *object)
1208 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1210 if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0)
1212 g_signal_handler_disconnect (data_sheet->clip,
1213 data_sheet->on_owner_change_signal);
1214 data_sheet->on_owner_change_signal = 0;
1217 if (data_sheet->dispose_has_run)
1220 data_sheet->dispose_has_run = TRUE;
1222 psppire_data_sheet_unset_data_store (data_sheet);
1224 g_object_unref (data_sheet->builder);
1226 if (data_sheet->uim)
1227 g_object_unref (data_sheet->uim);
1229 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1233 psppire_data_sheet_map (GtkWidget *widget)
1235 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1237 GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
1239 data_sheet->clip = gtk_widget_get_clipboard (widget,
1240 GDK_SELECTION_CLIPBOARD);
1241 if (data_sheet->on_owner_change_signal)
1242 g_signal_handler_disconnect (data_sheet->clip,
1243 data_sheet->on_owner_change_signal);
1244 data_sheet->on_owner_change_signal
1245 = g_signal_connect (data_sheet->clip, "owner-change",
1246 G_CALLBACK (on_owner_change), widget);
1247 on_owner_change (data_sheet->clip, NULL, widget);
1251 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1253 GObjectClass *gobject_class;
1254 GtkWidgetClass *widget_class;
1256 gobject_class = G_OBJECT_CLASS (class);
1257 gobject_class->set_property = psppire_data_sheet_set_property;
1258 gobject_class->get_property = psppire_data_sheet_get_property;
1259 gobject_class->dispose = psppire_data_sheet_dispose;
1261 widget_class = GTK_WIDGET_CLASS (class);
1262 widget_class->map = psppire_data_sheet_map;
1264 g_signal_new ("var-double-clicked",
1265 G_OBJECT_CLASS_TYPE (gobject_class),
1268 g_signal_accumulator_true_handled, NULL,
1269 psppire_marshal_BOOLEAN__INT,
1270 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1272 g_object_class_install_property (
1273 gobject_class, PROP_DATA_STORE,
1274 g_param_spec_object ("data-store",
1276 "The data store for the data sheet to display.",
1277 PSPPIRE_TYPE_DATA_STORE,
1278 G_PARAM_WRITABLE | G_PARAM_READABLE));
1280 g_object_class_install_property (
1281 gobject_class, PROP_VALUE_LABELS,
1282 g_param_spec_boolean ("value-labels",
1284 "Whether or not the data sheet should display labels instead of values",
1286 G_PARAM_WRITABLE | G_PARAM_READABLE));
1288 g_object_class_install_property (
1289 gobject_class, PROP_CASE_NUMBERS,
1290 g_param_spec_boolean ("case-numbers",
1292 "Whether or not the data sheet should display case numbers",
1294 G_PARAM_WRITABLE | G_PARAM_READABLE));
1296 g_object_class_install_property (
1299 g_param_spec_long ("current-case",
1301 "Zero based number of the selected case",
1304 G_PARAM_WRITABLE | G_PARAM_READABLE));
1306 g_object_class_install_property (
1308 PROP_MAY_CREATE_VARS,
1309 g_param_spec_boolean ("may-create-vars",
1310 "May create variables",
1311 "Whether the user may create more variables",
1313 G_PARAM_READWRITE));
1315 g_object_class_install_property (
1317 PROP_MAY_DELETE_VARS,
1318 g_param_spec_boolean ("may-delete-vars",
1319 "May delete variables",
1320 "Whether the user may delete variables",
1322 G_PARAM_READWRITE));
1324 g_object_class_install_property (
1327 g_param_spec_object ("ui-manager",
1329 "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.",
1330 GTK_TYPE_UI_MANAGER,
1335 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1337 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1340 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1341 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1345 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1347 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1351 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1352 gpointer user_data UNUSED)
1354 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1356 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1358 PsppSheetSelection *selection;
1360 selection = pspp_sheet_view_get_selection (sheet_view);
1361 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1365 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1366 &path, NULL, NULL, NULL))
1368 pspp_sheet_selection_unselect_all (selection);
1369 pspp_sheet_selection_select_path (selection, path);
1370 pspp_sheet_selection_select_all_columns (selection);
1371 gtk_tree_path_free (path);
1375 do_popup_menu (widget, event->button, event->time);
1384 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1386 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1387 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1388 const struct range_set_node *node;
1389 struct range_set *selected;
1391 selected = pspp_sheet_selection_get_range_set (selection);
1392 for (node = range_set_last (selected); node != NULL;
1393 node = range_set_prev (selected, node))
1395 unsigned long int start = range_set_node_get_start (node);
1396 unsigned long int count = range_set_node_get_width (node);
1398 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1400 range_set_destroy (selected);
1404 on_selection_changed (PsppSheetSelection *selection,
1405 gpointer user_data UNUSED)
1407 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1408 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1409 gint n_selected_rows;
1410 gboolean any_variables_selected;
1411 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1416 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1418 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1419 gtk_action_set_sensitive (action, n_selected_rows > 0);
1421 switch (n_selected_rows)
1424 may_delete_cases = FALSE;
1428 /* The row used for inserting new cases cannot be deleted. */
1429 path = gtk_tree_path_new_from_indices (
1430 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1431 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1433 gtk_tree_path_free (path);
1437 may_delete_cases = TRUE;
1440 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1441 gtk_action_set_sensitive (action, may_delete_cases);
1443 any_variables_selected = FALSE;
1444 may_delete_vars = may_insert_vars = FALSE;
1445 list = pspp_sheet_selection_get_selected_columns (selection);
1446 for (iter = list; iter != NULL; iter = iter->next)
1448 PsppSheetViewColumn *column = iter->data;
1449 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1453 may_delete_vars = may_insert_vars = TRUE;
1454 any_variables_selected = TRUE;
1457 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1458 may_insert_vars = TRUE;
1462 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1463 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1465 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1466 gtk_action_set_sensitive (action, may_insert_vars);
1468 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1469 gtk_action_set_sensitive (action, may_delete_vars);
1471 action = get_action_assert (data_sheet->builder, "sort-up");
1472 gtk_action_set_sensitive (action, may_delete_vars);
1474 action = get_action_assert (data_sheet->builder, "sort-down");
1475 gtk_action_set_sensitive (action, may_delete_vars);
1477 psppire_data_sheet_update_clip_actions (data_sheet);
1478 psppire_data_sheet_update_primary_selection (data_sheet,
1479 (n_selected_rows > 0
1480 && any_variables_selected));
1484 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1485 struct range_set **rowsp,
1486 struct range_set **colsp)
1488 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1489 PsppireDataStore *data_store = data_sheet->data_store;
1490 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1491 unsigned long n_cases;
1492 struct range_set *rows, *cols;
1495 if (data_store == NULL)
1497 n_cases = psppire_data_store_get_case_count (data_store);
1499 rows = pspp_sheet_selection_get_range_set (selection);
1500 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1501 if (range_set_is_empty (rows))
1503 range_set_destroy (rows);
1507 cols = range_set_create ();
1508 list = pspp_sheet_selection_get_selected_columns (selection);
1509 for (iter = list; iter != NULL; iter = iter->next)
1511 PsppSheetViewColumn *column = iter->data;
1512 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1515 range_set_set1 (cols, var_get_dict_index (var), 1);
1518 if (range_set_is_empty (cols))
1520 range_set_destroy (rows);
1521 range_set_destroy (cols);
1531 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1533 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1534 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1535 PsppireDataStore *data_store = data_sheet->data_store;
1536 struct range_set *selected;
1539 selected = pspp_sheet_selection_get_range_set (selection);
1540 row = range_set_scan (selected, 0);
1541 range_set_destroy (selected);
1543 if (row <= psppire_data_store_get_case_count (data_store))
1544 psppire_data_store_insert_new_case (data_store, row);
1548 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1550 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1551 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1552 PsppireDict *dict = data_sheet->data_store->dict;
1553 PsppSheetViewColumn *column;
1554 struct variable *var;
1559 list = pspp_sheet_selection_get_selected_columns (selection);
1562 column = list->data;
1565 var = g_object_get_data (G_OBJECT (column), "variable");
1566 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1567 if (psppire_dict_generate_name (dict, name, sizeof name))
1568 psppire_dict_insert_variable (dict, index, name);
1572 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1574 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1575 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1576 PsppireDict *dict = data_sheet->data_store->dict;
1579 list = pspp_sheet_selection_get_selected_columns (selection);
1582 list = g_list_reverse (list);
1583 for (iter = list; iter; iter = iter->next)
1585 PsppSheetViewColumn *column = iter->data;
1586 struct variable *var;
1588 var = g_object_get_data (G_OBJECT (column), "variable");
1590 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1602 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1604 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1605 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1606 PsppireDataWindow *pdw;
1611 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1612 g_return_if_fail (pdw != NULL);
1614 list = pspp_sheet_selection_get_selected_columns (selection);
1616 syntax = g_string_new ("SORT CASES BY");
1618 for (iter = list; iter; iter = iter->next)
1620 PsppSheetViewColumn *column = iter->data;
1621 struct variable *var;
1623 var = g_object_get_data (G_OBJECT (column), "variable");
1626 g_string_append_printf (syntax, " %s", var_get_name (var));
1632 if (order == SORT_DESCEND)
1633 g_string_append (syntax, " (DOWN)");
1634 g_string_append_c (syntax, '.');
1635 execute_const_syntax_string (pdw, syntax->str);
1637 g_string_free (syntax, TRUE);
1641 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1643 do_sort (data_sheet, SORT_ASCEND);
1647 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1649 do_sort (data_sheet, SORT_DESCEND);
1653 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1655 goto_case_dialog (data_sheet);
1659 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1661 PsppireDataWindow *pdw;
1663 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1664 g_return_if_fail (pdw != NULL);
1670 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1672 psppire_data_sheet_set_clip (data_sheet, FALSE);
1676 on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet)
1678 psppire_data_sheet_set_clip (data_sheet, TRUE);
1682 on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet)
1684 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
1685 GtkClipboard *clipboard =
1686 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1688 gtk_clipboard_request_contents (clipboard,
1689 gdk_atom_intern ("UTF8_STRING", TRUE),
1690 psppire_data_sheet_clip_received_cb,
1695 psppire_data_sheet_init (PsppireDataSheet *obj)
1697 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1700 obj->show_value_labels = FALSE;
1701 obj->show_case_numbers = TRUE;
1702 obj->may_create_vars = TRUE;
1703 obj->may_delete_vars = TRUE;
1705 obj->owns_primary_selection = FALSE;
1707 obj->scroll_to_bottom_signal = 0;
1708 obj->scroll_to_right_signal = 0;
1709 obj->on_owner_change_signal = 0;
1710 obj->new_variable_column = NULL;
1711 obj->container = NULL;
1714 obj->dispose_has_run = FALSE;
1716 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1718 g_signal_connect (obj, "notify::model",
1719 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1721 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1722 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1723 PSPP_SHEET_SELECTION_RECTANGLE);
1725 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1726 g_signal_connect (obj, "query-tooltip",
1727 G_CALLBACK (on_query_tooltip), NULL);
1728 g_signal_connect (obj, "button-press-event",
1729 G_CALLBACK (on_button_pressed), NULL);
1730 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1732 obj->builder = builder_new ("data-sheet.ui");
1734 action = get_action_assert (obj->builder, "edit_clear-cases");
1735 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1737 gtk_action_set_sensitive (action, FALSE);
1738 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1739 "changed", G_CALLBACK (on_selection_changed), NULL);
1741 action = get_action_assert (obj->builder, "edit_insert-case");
1742 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1745 action = get_action_assert (obj->builder, "edit_insert-variable");
1746 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1749 action = get_action_assert (obj->builder, "edit_goto-case");
1750 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1753 action = get_action_assert (obj->builder, "edit_copy");
1754 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1756 action = get_action_assert (obj->builder, "edit_cut");
1757 g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj);
1759 action = get_action_assert (obj->builder, "edit_paste");
1760 g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj);
1762 action = get_action_assert (obj->builder, "edit_clear-variables");
1763 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1766 action = get_action_assert (obj->builder, "edit_find");
1767 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1769 action = get_action_assert (obj->builder, "sort-up");
1770 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1772 action = get_action_assert (obj->builder, "sort-down");
1773 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1778 psppire_data_sheet_new (void)
1780 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1784 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1786 return data_sheet->data_store;
1790 refresh_model (PsppireDataSheet *data_sheet)
1792 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1794 if (data_sheet->data_store != NULL)
1796 PsppireEmptyListStore *model;
1800 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1801 model = psppire_empty_list_store_new (n_rows);
1802 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1803 GTK_TREE_MODEL (model));
1804 g_object_unref (model);
1806 action = get_action_assert (data_sheet->builder, "edit_copy");
1807 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1813 on_case_inserted (PsppireDataStore *data_store, gint row,
1814 PsppireDataSheet *data_sheet)
1816 PsppireEmptyListStore *empty_list_store;
1817 GtkTreeModel *tree_model;
1820 g_return_if_fail (data_store == data_sheet->data_store);
1822 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1823 if (row == n_rows - 1)
1826 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1827 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1828 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1829 psppire_empty_list_store_row_inserted (empty_list_store, row);
1833 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1834 PsppireDataSheet *data_sheet)
1837 g_return_if_fail (data_store == data_sheet->data_store);
1841 /* This is a bit of a cop-out. We could do better, if it ever turns out
1842 that this performs too poorly. */
1843 refresh_model (data_sheet);
1847 PsppireEmptyListStore *empty_list_store;
1848 GtkTreeModel *tree_model;
1849 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1851 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1852 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1853 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1854 psppire_empty_list_store_row_deleted (empty_list_store, first);
1859 on_case_change (PsppireDataStore *data_store, gint row,
1860 PsppireDataSheet *data_sheet)
1862 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1864 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1865 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1869 on_backend_changed (PsppireDataStore *data_store,
1870 PsppireDataSheet *data_sheet)
1872 g_return_if_fail (data_store == data_sheet->data_store);
1873 refresh_model (data_sheet);
1877 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1878 PsppireDataSheet *data_sheet)
1880 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1881 PsppSheetViewColumn *column;
1882 struct variable *var;
1886 g_return_if_fail (data_sheet->data_store != NULL);
1887 g_return_if_fail (dict == data_sheet->data_store->dict);
1889 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1894 var = psppire_dict_get_variable (data_store->dict, dict_index);
1895 g_return_if_fail (var != NULL);
1897 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1898 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1899 if (display_width != var_get_display_width (var))
1901 gint base_width, incr_width;
1903 display_width = var_get_display_width (var);
1904 calc_width_conversion (data_sheet, &base_width, &incr_width);
1905 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1906 base_width, incr_width);
1907 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1912 on_variable_changed (PsppireDict *dict, int dict_index,
1913 guint what, const struct variable *oldvar,
1914 PsppireDataSheet *data_sheet)
1916 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1917 PsppSheetViewColumn *column;
1918 GtkCellRenderer *cell;
1919 struct variable *var;
1923 g_return_if_fail (data_sheet->data_store != NULL);
1924 g_return_if_fail (dict == data_sheet->data_store->dict);
1927 if (what & VAR_TRAIT_DISPLAY_WIDTH)
1928 on_variable_display_width_changed (dict, dict_index, data_sheet);
1930 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1936 var = psppire_dict_get_variable (data_store->dict, dict_index);
1937 g_return_if_fail (var != NULL);
1939 name = escape_underscores (var_get_name (var));
1940 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1941 pspp_sheet_view_column_set_title (column, name);
1944 cells = pspp_sheet_view_column_get_cell_renderers (column);
1945 g_return_if_fail (cells);
1947 g_list_free (cells);
1949 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1951 /* Stop editing before we delete and replace the cell renderers.
1952 Otherwise if this column is currently being edited, an eventual call
1953 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1954 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1956 It's possible that this is a bug in PsppSheetView, and it's possible
1957 that PsppSheetView inherits that from GtkTreeView, but I haven't
1958 investigated yet. */
1959 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1961 add_data_column_cell_renderer (data_sheet, column);
1966 on_variable_inserted (PsppireDict *dict, int var_index,
1967 PsppireDataSheet *data_sheet)
1969 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1970 gint base_width, incr_width;
1971 PsppSheetViewColumn *column;
1973 calc_width_conversion (data_sheet, &base_width, &incr_width);
1974 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1975 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1979 on_variable_deleted (PsppireDict *dict,
1980 const struct variable *var, int case_idx, int width,
1981 PsppireDataSheet *data_sheet)
1983 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1984 GList *columns, *iter;
1986 columns = pspp_sheet_view_get_columns (sheet_view);
1987 for (iter = columns; iter != NULL; iter = iter->next)
1989 PsppSheetViewColumn *column = iter->data;
1990 const struct variable *column_var;
1992 column_var = g_object_get_data (G_OBJECT (column), "variable");
1993 if (column_var == var)
1994 pspp_sheet_view_remove_column (sheet_view, column);
1996 g_list_free (columns);
2000 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
2002 PsppireDataStore *store = data_sheet->data_store;
2007 data_sheet->data_store = NULL;
2009 g_signal_handlers_disconnect_by_func (
2010 store, G_CALLBACK (on_backend_changed), data_sheet);
2011 g_signal_handlers_disconnect_by_func (
2012 store, G_CALLBACK (on_case_inserted), data_sheet);
2013 g_signal_handlers_disconnect_by_func (
2014 store, G_CALLBACK (on_cases_deleted), data_sheet);
2015 g_signal_handlers_disconnect_by_func (
2016 store, G_CALLBACK (on_case_change), data_sheet);
2018 g_signal_handlers_disconnect_by_func (
2019 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
2020 g_signal_handlers_disconnect_by_func (
2021 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
2022 g_signal_handlers_disconnect_by_func (
2023 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
2024 g_signal_handlers_disconnect_by_func (
2025 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
2027 g_object_unref (store);
2031 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
2032 PsppireDataStore *data_store)
2034 psppire_data_sheet_unset_data_store (data_sheet);
2036 data_sheet->data_store = data_store;
2037 if (data_store != NULL)
2039 g_object_ref (data_store);
2040 g_signal_connect (data_store, "backend-changed",
2041 G_CALLBACK (on_backend_changed), data_sheet);
2042 g_signal_connect (data_store, "case-inserted",
2043 G_CALLBACK (on_case_inserted), data_sheet);
2044 g_signal_connect (data_store, "cases-deleted",
2045 G_CALLBACK (on_cases_deleted), data_sheet);
2046 g_signal_connect (data_store, "case-changed",
2047 G_CALLBACK (on_case_change), data_sheet);
2049 /* XXX it's unclean to hook into the dict this way--what if the dict
2050 changes? As of this writing, though, nothing ever changes the
2051 data_store's dict. */
2052 g_signal_connect (data_store->dict, "variable-changed",
2053 G_CALLBACK (on_variable_changed),
2055 g_signal_connect (data_store->dict, "variable-inserted",
2056 G_CALLBACK (on_variable_inserted), data_sheet);
2057 g_signal_connect (data_store->dict, "variable-deleted",
2058 G_CALLBACK (on_variable_deleted), data_sheet);
2060 refresh_model (data_sheet);
2063 /* Clipboard stuff */
2065 /* A casereader and dictionary holding the data currently in the clip */
2066 static struct casereader *clip_datasheet = NULL;
2067 static struct dictionary *clip_dict = NULL;
2070 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2073 psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
2074 struct casereader **readerp,
2075 struct dictionary **dictp)
2077 struct casewriter *writer ;
2078 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2079 struct case_map *map = NULL;
2080 struct range_set *rows, *cols;
2081 const struct range_set_node *node;
2082 struct dictionary *dict;
2084 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2091 /* Construct clip dictionary. */
2092 *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
2093 RANGE_SET_FOR_EACH (node, cols)
2097 for (dict_index = node->start; dict_index < node->end; dict_index++)
2099 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2100 dict_clone_var_assert (dict, var);
2104 /* Construct clip data. */
2105 map = case_map_by_name (ds->dict->dict, dict);
2106 writer = autopaging_writer_create (dict_get_proto (dict));
2107 RANGE_SET_FOR_EACH (node, rows)
2109 unsigned long int row;
2111 for (row = node->start; row < node->end; row++)
2113 struct ccase *old = psppire_data_store_get_case (ds, row);
2115 casewriter_write (writer, case_map_execute (map, old));
2117 casewriter_force_error (writer);
2120 case_map_destroy (map);
2122 /* Clear data that we copied out, if we're doing a "cut". */
2123 if (cut && !casewriter_error (writer))
2125 RANGE_SET_FOR_EACH (node, rows)
2127 unsigned long int row;
2129 for (row = node->start; row < node->end; row++)
2131 const struct range_set_node *node2;
2133 RANGE_SET_FOR_EACH (node2, cols)
2137 for (dict_index = node2->start; dict_index < node2->end;
2140 struct variable *var;
2142 var = dict_get_var (ds->dict->dict, dict_index);
2143 psppire_data_store_set_string (ds, "", row,
2151 range_set_destroy (rows);
2152 range_set_destroy (cols);
2154 *readerp = casewriter_make_reader (writer);
2159 /* Set the clip from the currently selected range in DATA_SHEET. If CUT is
2160 true, clears the original data from DATA_SHEET, otherwise leaves the
2161 original data in-place. */
2163 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2166 struct casereader *reader;
2167 struct dictionary *dict;
2169 if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
2171 casereader_destroy (clip_datasheet);
2172 dict_destroy (clip_dict);
2174 clip_datasheet = reader;
2177 psppire_data_sheet_update_clipboard (data_sheet);
2188 /* Perform data_out for case CC, variable V, appending to STRING */
2190 data_out_g_string (GString *string, const struct variable *v,
2191 const struct ccase *cc)
2193 const struct fmt_spec *fs = var_get_print_format (v);
2194 const union value *val = case_data (cc, v);
2196 char *s = data_out (val, var_get_encoding (v), fs);
2198 g_string_append (string, s);
2204 clip_to_text (struct casereader *datasheet, struct dictionary *dict)
2209 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2210 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2211 const size_t var_cnt = dict_get_var_cnt (dict);
2213 string = g_string_sized_new (10 * val_cnt * case_cnt);
2215 for (r = 0 ; r < case_cnt ; ++r )
2220 cc = casereader_peek (datasheet, r);
2223 g_warning ("Clipboard seems to have inexplicably shrunk");
2227 for (c = 0 ; c < var_cnt ; ++c)
2229 const struct variable *v = dict_get_var (dict, c);
2230 data_out_g_string (string, v, cc);
2231 if ( c < val_cnt - 1 )
2232 g_string_append (string, "\t");
2236 g_string_append (string, "\n");
2246 clip_to_html (struct casereader *datasheet, struct dictionary *dict)
2251 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2252 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2253 const size_t var_cnt = dict_get_var_cnt (dict);
2255 /* Guestimate the size needed */
2256 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2258 g_string_append (string,
2259 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2261 g_string_append (string, "<table>\n");
2262 for (r = 0 ; r < case_cnt ; ++r )
2265 struct ccase *cc = casereader_peek (datasheet, r);
2268 g_warning ("Clipboard seems to have inexplicably shrunk");
2271 g_string_append (string, "<tr>\n");
2273 for (c = 0 ; c < var_cnt ; ++c)
2275 const struct variable *v = dict_get_var (dict, c);
2276 g_string_append (string, "<td>");
2277 data_out_g_string (string, v, cc);
2278 g_string_append (string, "</td>\n");
2281 g_string_append (string, "</tr>\n");
2285 g_string_append (string, "</table>\n");
2293 psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
2295 struct casereader *reader,
2296 struct dictionary *dict)
2298 GString *string = NULL;
2302 case SELECT_FMT_TEXT:
2303 string = clip_to_text (reader, dict);
2305 case SELECT_FMT_HTML:
2306 string = clip_to_html (reader, dict);
2309 g_assert_not_reached ();
2312 gtk_selection_data_set (selection_data, selection_data->target,
2314 (const guchar *) string->str, string->len);
2316 g_string_free (string, TRUE);
2320 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2321 GtkSelectionData *selection_data,
2325 psppire_data_sheet_clipboard_set (selection_data, info,
2326 clip_datasheet, clip_dict);
2330 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2333 dict_destroy (clip_dict);
2336 casereader_destroy (clip_datasheet);
2337 clip_datasheet = NULL;
2341 static const GtkTargetEntry targets[] = {
2342 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2343 { "STRING", 0, SELECT_FMT_TEXT },
2344 { "TEXT", 0, SELECT_FMT_TEXT },
2345 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2346 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2347 { "text/plain", 0, SELECT_FMT_TEXT },
2348 { "text/html", 0, SELECT_FMT_HTML }
2354 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2356 GtkClipboard *clipboard =
2357 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2358 GDK_SELECTION_CLIPBOARD);
2360 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2361 G_N_ELEMENTS (targets),
2362 psppire_data_sheet_clipboard_get_cb,
2363 psppire_data_sheet_clipboard_clear_cb,
2365 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2369 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2371 struct range_set *rows, *cols;
2375 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2378 range_set_destroy (rows);
2379 range_set_destroy (cols);
2382 action = get_action_assert (data_sheet->builder, "edit_copy");
2383 gtk_action_set_sensitive (action, enable);
2385 action = get_action_assert (data_sheet->builder, "edit_cut");
2386 gtk_action_set_sensitive (action, enable);
2390 psppire_data_sheet_primary_get_cb (GtkClipboard *clipboard,
2391 GtkSelectionData *selection_data,
2395 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2396 struct casereader *reader;
2397 struct dictionary *dict;
2399 if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
2401 psppire_data_sheet_clipboard_set (selection_data, info,
2403 casereader_destroy (reader);
2404 dict_destroy (dict);
2409 psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
2410 gboolean should_own)
2412 GtkClipboard *clipboard;
2413 GdkDisplay *display;
2415 display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
2416 clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
2417 g_return_if_fail (clipboard != NULL);
2419 if (data_sheet->owns_primary_selection && !should_own)
2421 data_sheet->owns_primary_selection = FALSE;
2422 gtk_clipboard_clear (clipboard);
2424 else if (should_own)
2425 data_sheet->owns_primary_selection =
2426 gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2427 psppire_data_sheet_primary_get_cb,
2428 NULL, G_OBJECT (data_sheet));
2431 /* A callback for when the clipboard contents have been received. */
2433 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2434 GtkSelectionData *sd,
2437 PsppireDataSheet *data_sheet = data;
2438 PsppireDataStore *store = data_sheet->data_store;
2439 struct range_set *rows, *cols;
2441 gint next_row, next_column;
2445 if ( sd->length < 0 )
2448 if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
2451 c = (char *) sd->data;
2453 /* Get the starting selected position in the data sheet. (Possibly we should
2454 only paste into the selected range if it's larger than one cell?) */
2455 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2457 next_row = range_set_first (rows)->start;
2458 first_column = next_column = range_set_first (cols)->start;
2459 range_set_destroy (rows);
2460 range_set_destroy (cols);
2462 g_return_if_fail (next_row >= 0);
2463 g_return_if_fail (next_column >= 0);
2465 while (count < sd->length)
2467 gint row = next_row;
2468 gint column = next_column;
2469 struct variable *var;
2472 while (*c != '\t' && *c != '\n' && count < sd->length)
2480 next_column = column + 1;
2482 else if ( *c == '\n')
2485 next_column = first_column;
2490 var = psppire_dict_get_variable (store->dict, column);
2492 psppire_data_store_set_string (store, s, row, var, FALSE);
2497 psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
2502 GtkAction *action = GTK_ACTION (data);
2503 gboolean compatible_target;
2506 compatible_target = FALSE;
2507 for (i = 0; i < G_N_ELEMENTS (targets); i++)
2509 GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
2512 for (j = 0; j < n_atoms; j++)
2513 if (target == atoms[j])
2515 compatible_target = TRUE;
2520 gtk_action_set_sensitive (action, compatible_target);
2521 g_object_unref (action);
2525 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2527 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2528 GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste");
2530 g_object_ref (action);
2531 gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb,