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 *vl;
341 GtkListStore *list_store;
343 list_store = gtk_list_store_new (1, G_TYPE_STRING);
344 for (vl = val_labs_first (labels); vl != NULL;
345 vl = val_labs_next (labels, vl))
349 gtk_list_store_append (list_store, &iter);
350 gtk_list_store_set (list_store, &iter,
351 0, val_lab_get_label (vl),
355 gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
356 GTK_TREE_MODEL (list_store));
357 g_object_unref (list_store);
362 scroll_to_bottom (GtkWidget *widget,
363 GtkRequisition *requisition,
364 gpointer unused UNUSED)
366 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
367 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
368 GtkAdjustment *vadjust;
370 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
371 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
373 if (data_sheet->scroll_to_bottom_signal)
375 g_signal_handler_disconnect (data_sheet,
376 data_sheet->scroll_to_bottom_signal);
377 data_sheet->scroll_to_bottom_signal = 0;
382 on_data_column_edited (GtkCellRendererText *cell,
387 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
388 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
389 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
390 GtkEditable *editable;
391 struct variable *var;
397 path = gtk_tree_path_new_from_string (path_string);
398 row = gtk_tree_path_get_indices (path)[0];
399 gtk_tree_path_free (path);
401 var = g_object_get_data (G_OBJECT (column), "variable");
403 new_row = row == psppire_data_store_get_case_count (data_store);
404 if (new_row && new_text[0] == '\0')
407 editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
408 g_return_if_fail (editable != NULL);
409 is_val_lab = (GTK_IS_COMBO_BOX (editable)
410 && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
411 g_object_unref (editable);
413 psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
415 if (new_row && !data_sheet->scroll_to_bottom_signal)
417 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
418 data_sheet->scroll_to_bottom_signal =
419 g_signal_connect (data_sheet, "size-request",
420 G_CALLBACK (scroll_to_bottom), NULL);
424 /* We could be more specific about what to redraw, if it seems
425 important for performance. */
426 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
431 scroll_to_right (GtkWidget *widget,
432 PsppireDataSheet *data_sheet)
434 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
435 PsppSheetViewColumn *column, *prev;
436 GList *columns, *iter;
440 columns = pspp_sheet_view_get_columns (sheet_view);
441 for (iter = columns; iter; iter = iter->next)
443 PsppSheetViewColumn *c = iter->data;
444 if (g_object_get_data (G_OBJECT (c), "new-var-column"))
451 g_list_free (columns);
456 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
462 pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
465 pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
466 gtk_tree_path_free (path);
470 if (data_sheet->scroll_to_right_signal)
472 g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
473 data_sheet->scroll_to_right_signal = 0;
478 on_new_variable_column_edited (GtkCellRendererText *cell,
483 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
484 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
485 PsppireDict *dict = data_store->dict;
486 struct variable *var;
491 if (new_text[0] == '\0')
493 /* User didn't enter anything so don't create a variable. */
497 path = gtk_tree_path_new_from_string (path_string);
498 row = gtk_tree_path_get_indices (path)[0];
499 gtk_tree_path_free (path);
501 if (!psppire_dict_generate_name (dict, name, sizeof name))
504 var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
506 g_return_if_fail (var != NULL);
508 psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
510 if (!data_sheet->scroll_to_right_signal)
512 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
513 data_sheet->scroll_to_right_signal =
514 g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
515 G_CALLBACK (scroll_to_right), data_sheet);
519 /* We could be more specific about what to redraw, if it seems
520 important for performance. */
521 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
526 calc_width_conversion (PsppireDataSheet *data_sheet,
527 gint *base_width, gint *incr_width)
529 GtkCellRenderer *cell;
532 cell = gtk_cell_renderer_text_new ();
533 w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
534 w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
535 *incr_width = MAX (1, (w10 - w1) / 9);
536 *base_width = MAX (0, w10 - *incr_width * 10);
537 g_object_ref_sink (cell);
538 g_object_unref (cell);
542 display_width_from_pixel_width (PsppireDataSheet *data_sheet,
545 gint base_width, incr_width;
547 calc_width_conversion (data_sheet, &base_width, &incr_width);
548 return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
552 display_width_to_pixel_width (PsppireDataSheet *data_sheet,
557 return base_width + incr_width * display_width;
561 on_data_column_resized (GObject *gobject,
565 PsppireDataSheet *data_sheet = user_data;
566 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
567 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
568 struct variable *var;
572 if (data_store == NULL)
575 pixel_width = pspp_sheet_view_column_get_width (column);
576 if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
578 /* Short-circuit the expensive display_width_from_pixel_width()
579 calculation, to make loading .sav files with 2000 columns visibly
584 var = g_object_get_data (G_OBJECT (column), "variable");
585 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
586 var_set_display_width (var, display_width);
590 do_data_column_popup_menu (PsppSheetViewColumn *column,
591 guint button, guint32 time)
593 GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
594 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
597 menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
598 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
602 on_data_column_popup_menu (PsppSheetViewColumn *column,
603 gpointer user_data UNUSED)
605 do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
609 on_column_button_press_event (PsppSheetViewColumn *column,
610 GdkEventButton *event,
611 gpointer user_data UNUSED)
613 PsppSheetSelection *selection;
614 PsppSheetView *sheet_view;
616 sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
618 g_return_val_if_fail (sheet_view != NULL, FALSE);
620 selection = pspp_sheet_view_get_selection (sheet_view);
621 g_return_val_if_fail (selection != NULL, FALSE);
623 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
625 do_data_column_popup_menu (column, event->button, event->time);
628 else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
630 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
631 struct variable *var;
633 var = g_object_get_data (G_OBJECT (column), "variable");
638 g_signal_emit_by_name (data_sheet, "var-double-clicked",
639 var_get_dict_index (var), &handled);
648 on_data_column_query_tooltip (PsppSheetViewColumn *column,
650 gpointer user_data UNUSED)
652 struct variable *var;
655 var = g_object_get_data (G_OBJECT (column), "variable");
656 g_return_val_if_fail (var != NULL, FALSE);
658 text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
659 gtk_tooltip_set_text (tooltip, text);
665 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
666 PsppSheetViewColumn *column)
668 GtkCellRenderer *cell;
669 struct variable *var;
671 var = g_object_get_data (G_OBJECT (column), "variable");
672 g_return_if_fail (var != NULL);
674 if (var_has_value_labels (var))
676 cell = gtk_cell_renderer_combo_new ();
677 g_object_set (G_OBJECT (cell),
683 cell = gtk_cell_renderer_text_new ();
685 g_signal_connect (cell, "editing-started",
686 G_CALLBACK (on_data_column_editing_started), NULL);
687 g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
689 g_object_set_data (G_OBJECT (cell), "column", column);
690 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
692 pspp_sheet_view_column_clear (column);
693 pspp_sheet_view_column_pack_start (column, cell, TRUE);
695 pspp_sheet_view_column_set_cell_data_func (
696 column, cell, render_data_cell, data_sheet, NULL);
699 static PsppSheetViewColumn *
700 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
701 gint base_width, gint incr_width)
703 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
704 struct variable *var;
705 PsppSheetViewColumn *column;
709 var = psppire_dict_get_variable (data_store->dict, dict_idx);
711 column = pspp_sheet_view_column_new ();
713 name = escape_underscores (var_get_name (var));
714 pspp_sheet_view_column_set_title (column, name);
717 g_object_set_data (G_OBJECT (column), "variable", var);
719 width = display_width_to_pixel_width (data_sheet,
720 var_get_display_width (var),
721 base_width, incr_width);
722 pspp_sheet_view_column_set_min_width (column, 10);
723 pspp_sheet_view_column_set_fixed_width (column, width);
724 pspp_sheet_view_column_set_resizable (column, TRUE);
726 pspp_sheet_view_column_set_clickable (column, TRUE);
727 g_signal_connect (column, "notify::width",
728 G_CALLBACK (on_data_column_resized), data_sheet);
730 g_signal_connect (column, "button-press-event",
731 G_CALLBACK (on_column_button_press_event),
733 g_signal_connect (column, "query-tooltip",
734 G_CALLBACK (on_data_column_query_tooltip), NULL);
735 g_signal_connect (column, "popup-menu",
736 G_CALLBACK (on_data_column_popup_menu), data_sheet);
738 add_data_column_cell_renderer (data_sheet, column);
744 make_new_variable_column (PsppireDataSheet *data_sheet,
745 gint base_width, gint incr_width)
747 PsppSheetViewColumn *column;
748 GtkCellRenderer *cell;
751 cell = gtk_cell_renderer_text_new ();
752 g_object_set (cell, "editable", TRUE, NULL);
754 g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
757 column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
758 g_object_set_data (G_OBJECT (column), "new-var-column", column);
760 width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
761 pspp_sheet_view_column_set_min_width (column, 10);
762 pspp_sheet_view_column_set_fixed_width (column, width);
763 pspp_sheet_view_column_set_tabbable (column, FALSE);
765 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
766 g_signal_connect (column, "button-press-event",
767 G_CALLBACK (on_column_button_press_event),
769 g_signal_connect (column, "popup-menu",
770 G_CALLBACK (on_data_column_popup_menu), data_sheet);
772 pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
774 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
775 data_sheet->new_variable_column = column;
779 psppire_data_sheet_model_changed (GObject *gobject,
783 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
784 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
785 PsppireDataStore *data_store;
787 /* Remove old columns. */
790 PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
794 pspp_sheet_view_remove_column (sheet_view, column);
796 data_sheet->new_variable_column = NULL;
798 if (pspp_sheet_view_get_model (sheet_view) == NULL)
800 /* Don't create any columns at all if there's no model. Otherwise we'll
801 create some columns as part of the "dispose" callback for the sheet
802 view, which sets the model to NULL. That causes warnings to be
803 logged and is obviously undesirable in any case. */
807 /* Add new columns. */
808 data_store = psppire_data_sheet_get_data_store (data_sheet);
809 if (data_store != NULL)
811 gint base_width, incr_width;
814 calc_width_conversion (data_sheet, &base_width, &incr_width);
816 make_row_number_column (data_sheet, data_store);
817 for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
819 PsppSheetViewColumn *column;
821 column = make_data_column (data_sheet, i, base_width, incr_width);
822 pspp_sheet_view_append_column (sheet_view, column);
824 make_new_variable_column (data_sheet, base_width, incr_width);
835 PROP_MAY_CREATE_VARS,
836 PROP_MAY_DELETE_VARS,
841 psppire_data_sheet_set_property (GObject *object,
846 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
850 case PROP_DATA_STORE:
851 psppire_data_sheet_set_data_store (
852 obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
855 case PROP_VALUE_LABELS:
856 psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
859 case PROP_CASE_NUMBERS:
860 psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
863 case PROP_CURRENT_CASE:
864 psppire_data_sheet_goto_case (obj, g_value_get_long (value));
867 case PROP_MAY_CREATE_VARS:
868 psppire_data_sheet_set_may_create_vars (obj,
869 g_value_get_boolean (value));
872 case PROP_MAY_DELETE_VARS:
873 psppire_data_sheet_set_may_delete_vars (obj,
874 g_value_get_boolean (value));
877 case PROP_UI_MANAGER:
879 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
885 psppire_data_sheet_get_property (GObject *object,
890 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
894 case PROP_DATA_STORE:
895 g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
898 case PROP_VALUE_LABELS:
899 g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
902 case PROP_CASE_NUMBERS:
903 g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
906 case PROP_CURRENT_CASE:
907 g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
910 case PROP_MAY_CREATE_VARS:
911 g_value_set_boolean (value, obj->may_create_vars);
914 case PROP_MAY_DELETE_VARS:
915 g_value_set_boolean (value, obj->may_delete_vars);
918 case PROP_UI_MANAGER:
919 g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
923 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
929 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
931 return ds->show_value_labels;
935 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
936 gboolean show_value_labels)
938 show_value_labels = !!show_value_labels;
939 if (show_value_labels != ds->show_value_labels)
941 ds->show_value_labels = show_value_labels;
942 g_object_notify (G_OBJECT (ds), "value-labels");
943 gtk_widget_queue_draw (GTK_WIDGET (ds));
945 /* Make the cell being edited refresh too. */
946 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (ds), TRUE);
951 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
953 return ds->show_case_numbers;
957 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
958 gboolean show_case_numbers)
960 show_case_numbers = !!show_case_numbers;
961 if (show_case_numbers != ds->show_case_numbers)
963 PsppSheetViewColumn *column;
965 ds->show_case_numbers = show_case_numbers;
966 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
968 pspp_sheet_view_column_set_visible (column, show_case_numbers);
970 g_object_notify (G_OBJECT (ds), "case-numbers");
971 gtk_widget_queue_draw (GTK_WIDGET (ds));
976 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
978 return data_sheet->may_create_vars;
982 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
983 gboolean may_create_vars)
985 if (data_sheet->may_create_vars != may_create_vars)
987 data_sheet->may_create_vars = may_create_vars;
988 if (data_sheet->new_variable_column)
989 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
992 on_selection_changed (pspp_sheet_view_get_selection (
993 PSPP_SHEET_VIEW (data_sheet)), NULL);
998 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
1000 return data_sheet->may_delete_vars;
1004 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
1005 gboolean may_delete_vars)
1007 if (data_sheet->may_delete_vars != may_delete_vars)
1009 data_sheet->may_delete_vars = may_delete_vars;
1010 on_selection_changed (pspp_sheet_view_get_selection (
1011 PSPP_SHEET_VIEW (data_sheet)), NULL);
1015 static PsppSheetViewColumn *
1016 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1019 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1020 PsppireDataStore *data_store;
1021 PsppSheetViewColumn *column;
1022 struct variable *var;
1025 data_store = psppire_data_sheet_get_data_store (data_sheet);
1026 g_return_val_if_fail (data_store != NULL, NULL);
1027 g_return_val_if_fail (data_store->dict != NULL, NULL);
1029 var = psppire_dict_get_variable (data_store->dict, dict_index);
1030 g_return_val_if_fail (var != NULL, NULL);
1033 list = pspp_sheet_view_get_columns (sheet_view);
1034 for (iter = list; iter != NULL; iter = iter->next)
1036 PsppSheetViewColumn *c = iter->data;
1039 v = g_object_get_data (G_OBJECT (c), "variable");
1052 psppire_data_sheet_goto_variable (PsppireDataSheet *data_sheet,
1055 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1056 PsppSheetViewColumn *column;
1058 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1064 gint row = psppire_data_sheet_get_current_case (data_sheet);
1065 path = gtk_tree_path_new_from_indices (row >= 0 ? row : 0, -1);
1067 pspp_sheet_view_scroll_to_cell (sheet_view, path, column,
1069 pspp_sheet_view_set_cursor (sheet_view, path, column, FALSE);
1070 gtk_tree_path_free (path);
1075 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1077 PsppSheetSelection *selection;
1078 struct variable *var;
1079 GList *selected_columns;
1082 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1083 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1086 for (iter = selected_columns; iter != NULL; iter = iter->next)
1088 PsppSheetViewColumn *column = iter->data;
1089 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1102 g_list_free (selected_columns);
1108 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1110 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1111 PsppireDataStore *store = data_sheet->data_store;
1112 PsppSheetSelection *selection;
1115 g_return_if_fail (case_index >= 0);
1116 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1118 path = gtk_tree_path_new_from_indices (case_index, -1);
1120 /* Select the case. */
1121 selection = pspp_sheet_view_get_selection (sheet_view);
1122 pspp_sheet_selection_unselect_all (selection);
1123 pspp_sheet_selection_select_path (selection, path);
1124 pspp_sheet_selection_select_all_columns (selection);
1126 /* Scroll so that the case is visible. */
1127 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1129 gtk_tree_path_free (path);
1132 /* Returns the 0-based index of a selected case, if there is at least one, and
1135 If more than one case is selected, returns the one with the smallest index,
1136 that is, the index of the case closest to the beginning of the file. The
1137 row that can be used to insert a new case is not considered a case. */
1139 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1141 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1142 PsppireDataStore *store = data_sheet->data_store;
1143 const struct range_set_node *node;
1144 PsppSheetSelection *selection;
1145 struct range_set *rows;
1148 selection = pspp_sheet_view_get_selection (sheet_view);
1149 rows = pspp_sheet_selection_get_range_set (selection);
1150 node = range_set_first (rows);
1151 row = (node && node->start < psppire_data_store_get_case_count (store)
1154 range_set_destroy (rows);
1159 /* Returns the 0-based index of a selected case, if exactly one case is
1160 selected, and -1 otherwise. Returns -1 if the row that can be used to
1161 insert a new case is selected. */
1163 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1165 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1166 PsppireDataStore *store = data_sheet->data_store;
1167 const struct range_set_node *node;
1168 PsppSheetSelection *selection;
1169 struct range_set *rows;
1172 selection = pspp_sheet_view_get_selection (sheet_view);
1173 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1176 rows = pspp_sheet_selection_get_range_set (selection);
1177 node = range_set_first (rows);
1178 row = (node && node->start < psppire_data_store_get_case_count (store)
1181 range_set_destroy (rows);
1187 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1189 if (data_sheet->uim == NULL)
1192 GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1194 GTK_TYPE_UI_MANAGER));
1195 g_object_ref (data_sheet->uim);
1198 return data_sheet->uim;
1202 psppire_data_sheet_dispose (GObject *object)
1204 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1206 if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0)
1208 g_signal_handler_disconnect (data_sheet->clip,
1209 data_sheet->on_owner_change_signal);
1210 data_sheet->on_owner_change_signal = 0;
1213 if (data_sheet->dispose_has_run)
1216 data_sheet->dispose_has_run = TRUE;
1218 psppire_data_sheet_unset_data_store (data_sheet);
1220 g_object_unref (data_sheet->builder);
1222 if (data_sheet->uim)
1223 g_object_unref (data_sheet->uim);
1225 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1229 psppire_data_sheet_map (GtkWidget *widget)
1231 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1233 GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
1235 data_sheet->clip = gtk_widget_get_clipboard (widget,
1236 GDK_SELECTION_CLIPBOARD);
1237 if (data_sheet->on_owner_change_signal)
1238 g_signal_handler_disconnect (data_sheet->clip,
1239 data_sheet->on_owner_change_signal);
1240 data_sheet->on_owner_change_signal
1241 = g_signal_connect (data_sheet->clip, "owner-change",
1242 G_CALLBACK (on_owner_change), widget);
1243 on_owner_change (data_sheet->clip, NULL, widget);
1247 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1249 GObjectClass *gobject_class;
1250 GtkWidgetClass *widget_class;
1252 gobject_class = G_OBJECT_CLASS (class);
1253 gobject_class->set_property = psppire_data_sheet_set_property;
1254 gobject_class->get_property = psppire_data_sheet_get_property;
1255 gobject_class->dispose = psppire_data_sheet_dispose;
1257 widget_class = GTK_WIDGET_CLASS (class);
1258 widget_class->map = psppire_data_sheet_map;
1260 g_signal_new ("var-double-clicked",
1261 G_OBJECT_CLASS_TYPE (gobject_class),
1264 g_signal_accumulator_true_handled, NULL,
1265 psppire_marshal_BOOLEAN__INT,
1266 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1268 g_object_class_install_property (
1269 gobject_class, PROP_DATA_STORE,
1270 g_param_spec_object ("data-store",
1272 "The data store for the data sheet to display.",
1273 PSPPIRE_TYPE_DATA_STORE,
1274 G_PARAM_WRITABLE | G_PARAM_READABLE));
1276 g_object_class_install_property (
1277 gobject_class, PROP_VALUE_LABELS,
1278 g_param_spec_boolean ("value-labels",
1280 "Whether or not the data sheet should display labels instead of values",
1282 G_PARAM_WRITABLE | G_PARAM_READABLE));
1284 g_object_class_install_property (
1285 gobject_class, PROP_CASE_NUMBERS,
1286 g_param_spec_boolean ("case-numbers",
1288 "Whether or not the data sheet should display case numbers",
1290 G_PARAM_WRITABLE | G_PARAM_READABLE));
1292 g_object_class_install_property (
1295 g_param_spec_long ("current-case",
1297 "Zero based number of the selected case",
1300 G_PARAM_WRITABLE | G_PARAM_READABLE));
1302 g_object_class_install_property (
1304 PROP_MAY_CREATE_VARS,
1305 g_param_spec_boolean ("may-create-vars",
1306 "May create variables",
1307 "Whether the user may create more variables",
1309 G_PARAM_READWRITE));
1311 g_object_class_install_property (
1313 PROP_MAY_DELETE_VARS,
1314 g_param_spec_boolean ("may-delete-vars",
1315 "May delete variables",
1316 "Whether the user may delete variables",
1318 G_PARAM_READWRITE));
1320 g_object_class_install_property (
1323 g_param_spec_object ("ui-manager",
1325 "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.",
1326 GTK_TYPE_UI_MANAGER,
1331 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1333 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1336 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1337 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1341 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1343 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1347 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1348 gpointer user_data UNUSED)
1350 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1352 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1354 PsppSheetSelection *selection;
1356 selection = pspp_sheet_view_get_selection (sheet_view);
1357 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1361 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1362 &path, NULL, NULL, NULL))
1364 pspp_sheet_selection_unselect_all (selection);
1365 pspp_sheet_selection_select_path (selection, path);
1366 pspp_sheet_selection_select_all_columns (selection);
1367 gtk_tree_path_free (path);
1371 do_popup_menu (widget, event->button, event->time);
1380 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1382 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1383 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1384 const struct range_set_node *node;
1385 struct range_set *selected;
1387 selected = pspp_sheet_selection_get_range_set (selection);
1388 for (node = range_set_last (selected); node != NULL;
1389 node = range_set_prev (selected, node))
1391 unsigned long int start = range_set_node_get_start (node);
1392 unsigned long int count = range_set_node_get_width (node);
1394 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1396 range_set_destroy (selected);
1400 on_selection_changed (PsppSheetSelection *selection,
1401 gpointer user_data UNUSED)
1403 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1404 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1405 gint n_selected_rows;
1406 gboolean any_variables_selected;
1407 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1412 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1414 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1415 gtk_action_set_sensitive (action, n_selected_rows > 0);
1417 switch (n_selected_rows)
1420 may_delete_cases = FALSE;
1424 /* The row used for inserting new cases cannot be deleted. */
1425 path = gtk_tree_path_new_from_indices (
1426 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1427 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1429 gtk_tree_path_free (path);
1433 may_delete_cases = TRUE;
1436 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1437 gtk_action_set_sensitive (action, may_delete_cases);
1439 any_variables_selected = FALSE;
1440 may_delete_vars = may_insert_vars = FALSE;
1441 list = pspp_sheet_selection_get_selected_columns (selection);
1442 for (iter = list; iter != NULL; iter = iter->next)
1444 PsppSheetViewColumn *column = iter->data;
1445 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1449 may_delete_vars = may_insert_vars = TRUE;
1450 any_variables_selected = TRUE;
1453 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1454 may_insert_vars = TRUE;
1458 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1459 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1461 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1462 gtk_action_set_sensitive (action, may_insert_vars);
1464 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1465 gtk_action_set_sensitive (action, may_delete_vars);
1467 action = get_action_assert (data_sheet->builder, "sort-up");
1468 gtk_action_set_sensitive (action, may_delete_vars);
1470 action = get_action_assert (data_sheet->builder, "sort-down");
1471 gtk_action_set_sensitive (action, may_delete_vars);
1473 psppire_data_sheet_update_clip_actions (data_sheet);
1474 psppire_data_sheet_update_primary_selection (data_sheet,
1475 (n_selected_rows > 0
1476 && any_variables_selected));
1480 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1481 struct range_set **rowsp,
1482 struct range_set **colsp)
1484 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1485 PsppireDataStore *data_store = data_sheet->data_store;
1486 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1487 unsigned long n_cases;
1488 struct range_set *rows, *cols;
1491 if (data_store == NULL)
1493 n_cases = psppire_data_store_get_case_count (data_store);
1495 rows = pspp_sheet_selection_get_range_set (selection);
1496 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1497 if (range_set_is_empty (rows))
1499 range_set_destroy (rows);
1503 cols = range_set_create ();
1504 list = pspp_sheet_selection_get_selected_columns (selection);
1505 for (iter = list; iter != NULL; iter = iter->next)
1507 PsppSheetViewColumn *column = iter->data;
1508 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1511 range_set_set1 (cols, var_get_dict_index (var), 1);
1514 if (range_set_is_empty (cols))
1516 range_set_destroy (rows);
1517 range_set_destroy (cols);
1527 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1529 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1530 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1531 PsppireDataStore *data_store = data_sheet->data_store;
1532 struct range_set *selected;
1535 selected = pspp_sheet_selection_get_range_set (selection);
1536 row = range_set_scan (selected, 0);
1537 range_set_destroy (selected);
1539 if (row <= psppire_data_store_get_case_count (data_store))
1540 psppire_data_store_insert_new_case (data_store, row);
1544 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1546 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1547 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1548 PsppireDict *dict = data_sheet->data_store->dict;
1549 PsppSheetViewColumn *column;
1550 struct variable *var;
1555 list = pspp_sheet_selection_get_selected_columns (selection);
1558 column = list->data;
1561 var = g_object_get_data (G_OBJECT (column), "variable");
1562 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1563 if (psppire_dict_generate_name (dict, name, sizeof name))
1564 psppire_dict_insert_variable (dict, index, name);
1568 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1570 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1571 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1572 PsppireDict *dict = data_sheet->data_store->dict;
1575 list = pspp_sheet_selection_get_selected_columns (selection);
1578 list = g_list_reverse (list);
1579 for (iter = list; iter; iter = iter->next)
1581 PsppSheetViewColumn *column = iter->data;
1582 struct variable *var;
1584 var = g_object_get_data (G_OBJECT (column), "variable");
1586 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1598 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1600 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1601 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1602 PsppireDataWindow *pdw;
1607 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1608 g_return_if_fail (pdw != NULL);
1610 list = pspp_sheet_selection_get_selected_columns (selection);
1612 syntax = g_string_new ("SORT CASES BY");
1614 for (iter = list; iter; iter = iter->next)
1616 PsppSheetViewColumn *column = iter->data;
1617 struct variable *var;
1619 var = g_object_get_data (G_OBJECT (column), "variable");
1622 g_string_append_printf (syntax, " %s", var_get_name (var));
1628 if (order == SORT_DESCEND)
1629 g_string_append (syntax, " (DOWN)");
1630 g_string_append_c (syntax, '.');
1631 execute_const_syntax_string (pdw, syntax->str);
1633 g_string_free (syntax, TRUE);
1637 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1639 do_sort (data_sheet, SORT_ASCEND);
1643 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1645 do_sort (data_sheet, SORT_DESCEND);
1649 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1651 goto_case_dialog (data_sheet);
1655 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1657 PsppireDataWindow *pdw;
1659 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1660 g_return_if_fail (pdw != NULL);
1666 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1668 psppire_data_sheet_set_clip (data_sheet, FALSE);
1672 on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet)
1674 psppire_data_sheet_set_clip (data_sheet, TRUE);
1678 on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet)
1680 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
1681 GtkClipboard *clipboard =
1682 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1684 gtk_clipboard_request_contents (clipboard,
1685 gdk_atom_intern ("UTF8_STRING", TRUE),
1686 psppire_data_sheet_clip_received_cb,
1691 psppire_data_sheet_init (PsppireDataSheet *obj)
1693 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1696 obj->show_value_labels = FALSE;
1697 obj->show_case_numbers = TRUE;
1698 obj->may_create_vars = TRUE;
1699 obj->may_delete_vars = TRUE;
1701 obj->owns_primary_selection = FALSE;
1703 obj->scroll_to_bottom_signal = 0;
1704 obj->scroll_to_right_signal = 0;
1705 obj->on_owner_change_signal = 0;
1706 obj->new_variable_column = NULL;
1707 obj->container = NULL;
1710 obj->dispose_has_run = FALSE;
1712 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1714 g_signal_connect (obj, "notify::model",
1715 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1717 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1718 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1719 PSPP_SHEET_SELECTION_RECTANGLE);
1721 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1722 g_signal_connect (obj, "query-tooltip",
1723 G_CALLBACK (on_query_tooltip), NULL);
1724 g_signal_connect (obj, "button-press-event",
1725 G_CALLBACK (on_button_pressed), NULL);
1726 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1728 obj->builder = builder_new ("data-sheet.ui");
1730 action = get_action_assert (obj->builder, "edit_clear-cases");
1731 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1733 gtk_action_set_sensitive (action, FALSE);
1734 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1735 "changed", G_CALLBACK (on_selection_changed), NULL);
1737 action = get_action_assert (obj->builder, "edit_insert-case");
1738 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1741 action = get_action_assert (obj->builder, "edit_insert-variable");
1742 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1745 action = get_action_assert (obj->builder, "edit_goto-case");
1746 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1749 action = get_action_assert (obj->builder, "edit_copy");
1750 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1752 action = get_action_assert (obj->builder, "edit_cut");
1753 g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj);
1755 action = get_action_assert (obj->builder, "edit_paste");
1756 g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj);
1758 action = get_action_assert (obj->builder, "edit_clear-variables");
1759 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1762 action = get_action_assert (obj->builder, "edit_find");
1763 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1765 action = get_action_assert (obj->builder, "sort-up");
1766 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1768 action = get_action_assert (obj->builder, "sort-down");
1769 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1774 psppire_data_sheet_new (void)
1776 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1780 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1782 return data_sheet->data_store;
1786 refresh_model (PsppireDataSheet *data_sheet)
1788 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1790 if (data_sheet->data_store != NULL)
1792 PsppireEmptyListStore *model;
1796 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1797 model = psppire_empty_list_store_new (n_rows);
1798 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1799 GTK_TREE_MODEL (model));
1800 g_object_unref (model);
1802 action = get_action_assert (data_sheet->builder, "edit_copy");
1803 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1809 on_case_inserted (PsppireDataStore *data_store, gint row,
1810 PsppireDataSheet *data_sheet)
1812 PsppireEmptyListStore *empty_list_store;
1813 GtkTreeModel *tree_model;
1816 g_return_if_fail (data_store == data_sheet->data_store);
1818 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1819 if (row == n_rows - 1)
1822 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1823 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1824 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1825 psppire_empty_list_store_row_inserted (empty_list_store, row);
1829 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1830 PsppireDataSheet *data_sheet)
1833 g_return_if_fail (data_store == data_sheet->data_store);
1837 /* This is a bit of a cop-out. We could do better, if it ever turns out
1838 that this performs too poorly. */
1839 refresh_model (data_sheet);
1843 PsppireEmptyListStore *empty_list_store;
1844 GtkTreeModel *tree_model;
1845 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1847 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1848 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1849 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1850 psppire_empty_list_store_row_deleted (empty_list_store, first);
1855 on_case_change (PsppireDataStore *data_store, gint row,
1856 PsppireDataSheet *data_sheet)
1858 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1860 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1861 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1865 on_backend_changed (PsppireDataStore *data_store,
1866 PsppireDataSheet *data_sheet)
1868 g_return_if_fail (data_store == data_sheet->data_store);
1869 refresh_model (data_sheet);
1873 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1874 PsppireDataSheet *data_sheet)
1876 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1877 PsppSheetViewColumn *column;
1878 struct variable *var;
1882 g_return_if_fail (data_sheet->data_store != NULL);
1883 g_return_if_fail (dict == data_sheet->data_store->dict);
1885 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1890 var = psppire_dict_get_variable (data_store->dict, dict_index);
1891 g_return_if_fail (var != NULL);
1893 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1894 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1895 if (display_width != var_get_display_width (var))
1897 gint base_width, incr_width;
1899 display_width = var_get_display_width (var);
1900 calc_width_conversion (data_sheet, &base_width, &incr_width);
1901 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1902 base_width, incr_width);
1903 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1908 on_variable_changed (PsppireDict *dict, int dict_index,
1909 PsppireDataSheet *data_sheet)
1911 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1912 PsppSheetViewColumn *column;
1913 GtkCellRenderer *cell;
1914 struct variable *var;
1918 g_return_if_fail (data_sheet->data_store != NULL);
1919 g_return_if_fail (dict == data_sheet->data_store->dict);
1921 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1926 var = psppire_dict_get_variable (data_store->dict, dict_index);
1927 g_return_if_fail (var != NULL);
1929 name = escape_underscores (var_get_name (var));
1930 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1931 pspp_sheet_view_column_set_title (column, name);
1934 cells = pspp_sheet_view_column_get_cell_renderers (column);
1935 g_return_if_fail (cells);
1937 g_list_free (cells);
1939 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1941 /* Stop editing before we delete and replace the cell renderers.
1942 Otherwise if this column is currently being edited, an eventual call
1943 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1944 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1946 It's possible that this is a bug in PsppSheetView, and it's possible
1947 that PsppSheetView inherits that from GtkTreeView, but I haven't
1948 investigated yet. */
1949 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1951 add_data_column_cell_renderer (data_sheet, column);
1956 on_variable_inserted (PsppireDict *dict, int var_index,
1957 PsppireDataSheet *data_sheet)
1959 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1960 gint base_width, incr_width;
1961 PsppSheetViewColumn *column;
1963 calc_width_conversion (data_sheet, &base_width, &incr_width);
1964 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1965 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1969 on_variable_deleted (PsppireDict *dict,
1970 const struct variable *var, int case_idx, int width,
1971 PsppireDataSheet *data_sheet)
1973 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1974 GList *columns, *iter;
1976 columns = pspp_sheet_view_get_columns (sheet_view);
1977 for (iter = columns; iter != NULL; iter = iter->next)
1979 PsppSheetViewColumn *column = iter->data;
1980 const struct variable *column_var;
1982 column_var = g_object_get_data (G_OBJECT (column), "variable");
1983 if (column_var == var)
1984 pspp_sheet_view_remove_column (sheet_view, column);
1986 g_list_free (columns);
1990 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1992 PsppireDataStore *store = data_sheet->data_store;
1997 data_sheet->data_store = NULL;
1999 g_signal_handlers_disconnect_by_func (
2000 store, G_CALLBACK (on_backend_changed), data_sheet);
2001 g_signal_handlers_disconnect_by_func (
2002 store, G_CALLBACK (on_case_inserted), data_sheet);
2003 g_signal_handlers_disconnect_by_func (
2004 store, G_CALLBACK (on_cases_deleted), data_sheet);
2005 g_signal_handlers_disconnect_by_func (
2006 store, G_CALLBACK (on_case_change), data_sheet);
2008 g_signal_handlers_disconnect_by_func (
2009 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
2010 g_signal_handlers_disconnect_by_func (
2011 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
2012 g_signal_handlers_disconnect_by_func (
2013 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
2014 g_signal_handlers_disconnect_by_func (
2015 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
2017 g_object_unref (store);
2021 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
2022 PsppireDataStore *data_store)
2024 psppire_data_sheet_unset_data_store (data_sheet);
2026 data_sheet->data_store = data_store;
2027 if (data_store != NULL)
2029 g_object_ref (data_store);
2030 g_signal_connect (data_store, "backend-changed",
2031 G_CALLBACK (on_backend_changed), data_sheet);
2032 g_signal_connect (data_store, "case-inserted",
2033 G_CALLBACK (on_case_inserted), data_sheet);
2034 g_signal_connect (data_store, "cases-deleted",
2035 G_CALLBACK (on_cases_deleted), data_sheet);
2036 g_signal_connect (data_store, "case-changed",
2037 G_CALLBACK (on_case_change), data_sheet);
2039 /* XXX it's unclean to hook into the dict this way--what if the dict
2040 changes? As of this writing, though, nothing ever changes the
2041 data_store's dict. */
2042 g_signal_connect (data_store->dict, "variable-changed",
2043 G_CALLBACK (on_variable_changed),
2045 g_signal_connect (data_store->dict, "variable-display-width-changed",
2046 G_CALLBACK (on_variable_display_width_changed),
2048 g_signal_connect (data_store->dict, "variable-inserted",
2049 G_CALLBACK (on_variable_inserted), data_sheet);
2050 g_signal_connect (data_store->dict, "variable-deleted",
2051 G_CALLBACK (on_variable_deleted), data_sheet);
2053 refresh_model (data_sheet);
2056 /* Clipboard stuff */
2058 /* A casereader and dictionary holding the data currently in the clip */
2059 static struct casereader *clip_datasheet = NULL;
2060 static struct dictionary *clip_dict = NULL;
2063 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2066 psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
2067 struct casereader **readerp,
2068 struct dictionary **dictp)
2070 struct casewriter *writer ;
2071 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2072 struct case_map *map = NULL;
2073 struct range_set *rows, *cols;
2074 const struct range_set_node *node;
2075 struct dictionary *dict;
2077 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2084 /* Construct clip dictionary. */
2085 *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
2086 RANGE_SET_FOR_EACH (node, cols)
2090 for (dict_index = node->start; dict_index < node->end; dict_index++)
2092 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2093 dict_clone_var_assert (dict, var);
2097 /* Construct clip data. */
2098 map = case_map_by_name (ds->dict->dict, dict);
2099 writer = autopaging_writer_create (dict_get_proto (dict));
2100 RANGE_SET_FOR_EACH (node, rows)
2102 unsigned long int row;
2104 for (row = node->start; row < node->end; row++)
2106 struct ccase *old = psppire_data_store_get_case (ds, row);
2108 casewriter_write (writer, case_map_execute (map, old));
2110 casewriter_force_error (writer);
2113 case_map_destroy (map);
2115 /* Clear data that we copied out, if we're doing a "cut". */
2116 if (cut && !casewriter_error (writer))
2118 RANGE_SET_FOR_EACH (node, rows)
2120 unsigned long int row;
2122 for (row = node->start; row < node->end; row++)
2124 const struct range_set_node *node2;
2126 RANGE_SET_FOR_EACH (node2, cols)
2130 for (dict_index = node2->start; dict_index < node2->end;
2133 struct variable *var;
2135 var = dict_get_var (ds->dict->dict, dict_index);
2136 psppire_data_store_set_string (ds, "", row,
2144 range_set_destroy (rows);
2145 range_set_destroy (cols);
2147 *readerp = casewriter_make_reader (writer);
2152 /* Set the clip from the currently selected range in DATA_SHEET. If CUT is
2153 true, clears the original data from DATA_SHEET, otherwise leaves the
2154 original data in-place. */
2156 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2159 struct casereader *reader;
2160 struct dictionary *dict;
2162 if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
2164 casereader_destroy (clip_datasheet);
2165 dict_destroy (clip_dict);
2167 clip_datasheet = reader;
2170 psppire_data_sheet_update_clipboard (data_sheet);
2181 /* Perform data_out for case CC, variable V, appending to STRING */
2183 data_out_g_string (GString *string, const struct variable *v,
2184 const struct ccase *cc)
2186 const struct fmt_spec *fs = var_get_print_format (v);
2187 const union value *val = case_data (cc, v);
2189 char *s = data_out (val, var_get_encoding (v), fs);
2191 g_string_append (string, s);
2197 clip_to_text (struct casereader *datasheet, struct dictionary *dict)
2202 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2203 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2204 const size_t var_cnt = dict_get_var_cnt (dict);
2206 string = g_string_sized_new (10 * val_cnt * case_cnt);
2208 for (r = 0 ; r < case_cnt ; ++r )
2213 cc = casereader_peek (datasheet, r);
2216 g_warning ("Clipboard seems to have inexplicably shrunk");
2220 for (c = 0 ; c < var_cnt ; ++c)
2222 const struct variable *v = dict_get_var (dict, c);
2223 data_out_g_string (string, v, cc);
2224 if ( c < val_cnt - 1 )
2225 g_string_append (string, "\t");
2229 g_string_append (string, "\n");
2239 clip_to_html (struct casereader *datasheet, struct dictionary *dict)
2244 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2245 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2246 const size_t var_cnt = dict_get_var_cnt (dict);
2248 /* Guestimate the size needed */
2249 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2251 g_string_append (string,
2252 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2254 g_string_append (string, "<table>\n");
2255 for (r = 0 ; r < case_cnt ; ++r )
2258 struct ccase *cc = casereader_peek (datasheet, r);
2261 g_warning ("Clipboard seems to have inexplicably shrunk");
2264 g_string_append (string, "<tr>\n");
2266 for (c = 0 ; c < var_cnt ; ++c)
2268 const struct variable *v = dict_get_var (dict, c);
2269 g_string_append (string, "<td>");
2270 data_out_g_string (string, v, cc);
2271 g_string_append (string, "</td>\n");
2274 g_string_append (string, "</tr>\n");
2278 g_string_append (string, "</table>\n");
2286 psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
2288 struct casereader *reader,
2289 struct dictionary *dict)
2291 GString *string = NULL;
2295 case SELECT_FMT_TEXT:
2296 string = clip_to_text (reader, dict);
2298 case SELECT_FMT_HTML:
2299 string = clip_to_html (reader, dict);
2302 g_assert_not_reached ();
2305 gtk_selection_data_set (selection_data, selection_data->target,
2307 (const guchar *) string->str, string->len);
2309 g_string_free (string, TRUE);
2313 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2314 GtkSelectionData *selection_data,
2318 psppire_data_sheet_clipboard_set (selection_data, info,
2319 clip_datasheet, clip_dict);
2323 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2326 dict_destroy (clip_dict);
2329 casereader_destroy (clip_datasheet);
2330 clip_datasheet = NULL;
2334 static const GtkTargetEntry targets[] = {
2335 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2336 { "STRING", 0, SELECT_FMT_TEXT },
2337 { "TEXT", 0, SELECT_FMT_TEXT },
2338 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2339 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2340 { "text/plain", 0, SELECT_FMT_TEXT },
2341 { "text/html", 0, SELECT_FMT_HTML }
2347 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2349 GtkClipboard *clipboard =
2350 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2351 GDK_SELECTION_CLIPBOARD);
2353 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2354 G_N_ELEMENTS (targets),
2355 psppire_data_sheet_clipboard_get_cb,
2356 psppire_data_sheet_clipboard_clear_cb,
2358 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2362 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2364 struct range_set *rows, *cols;
2368 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2371 range_set_destroy (rows);
2372 range_set_destroy (cols);
2375 action = get_action_assert (data_sheet->builder, "edit_copy");
2376 gtk_action_set_sensitive (action, enable);
2378 action = get_action_assert (data_sheet->builder, "edit_cut");
2379 gtk_action_set_sensitive (action, enable);
2383 psppire_data_sheet_primary_get_cb (GtkClipboard *clipboard,
2384 GtkSelectionData *selection_data,
2388 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2389 struct casereader *reader;
2390 struct dictionary *dict;
2392 if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
2394 psppire_data_sheet_clipboard_set (selection_data, info,
2396 casereader_destroy (reader);
2397 dict_destroy (dict);
2402 psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
2403 gboolean should_own)
2405 GtkClipboard *clipboard;
2406 GdkDisplay *display;
2408 display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
2409 clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
2410 g_return_if_fail (clipboard != NULL);
2412 if (data_sheet->owns_primary_selection && !should_own)
2414 data_sheet->owns_primary_selection = FALSE;
2415 gtk_clipboard_clear (clipboard);
2417 else if (should_own)
2418 data_sheet->owns_primary_selection =
2419 gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2420 psppire_data_sheet_primary_get_cb,
2421 NULL, G_OBJECT (data_sheet));
2424 /* A callback for when the clipboard contents have been received. */
2426 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2427 GtkSelectionData *sd,
2430 PsppireDataSheet *data_sheet = data;
2431 PsppireDataStore *store = data_sheet->data_store;
2432 struct range_set *rows, *cols;
2434 gint next_row, next_column;
2438 if ( sd->length < 0 )
2441 if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
2444 c = (char *) sd->data;
2446 /* Get the starting selected position in the data sheet. (Possibly we should
2447 only paste into the selected range if it's larger than one cell?) */
2448 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2450 next_row = range_set_first (rows)->start;
2451 first_column = next_column = range_set_first (cols)->start;
2452 range_set_destroy (rows);
2453 range_set_destroy (cols);
2455 g_return_if_fail (next_row >= 0);
2456 g_return_if_fail (next_column >= 0);
2458 while (count < sd->length)
2460 gint row = next_row;
2461 gint column = next_column;
2462 struct variable *var;
2465 while (*c != '\t' && *c != '\n' && count < sd->length)
2473 next_column = column + 1;
2475 else if ( *c == '\n')
2478 next_column = first_column;
2483 var = psppire_dict_get_variable (store->dict, column);
2485 psppire_data_store_set_string (store, s, row, var, FALSE);
2490 psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
2495 GtkAction *action = GTK_ACTION (data);
2496 gboolean compatible_target;
2499 compatible_target = FALSE;
2500 for (i = 0; i < G_N_ELEMENTS (targets); i++)
2502 GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
2505 for (j = 0; j < n_atoms; j++)
2506 if (target == atoms[j])
2508 compatible_target = TRUE;
2513 gtk_action_set_sensitive (action, compatible_target);
2514 g_object_unref (action);
2518 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2520 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2521 GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste");
2523 g_object_ref (action);
2524 gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb,