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 (data_sheet->show_value_labels && 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");
948 /* Pretend the model changed, to force the columns to be rebuilt.
949 Otherwise cell renderers won't get changed from combo boxes to text
950 entries or vice versa. */
951 g_object_notify (G_OBJECT (ds), "model");
956 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
958 return ds->show_case_numbers;
962 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
963 gboolean show_case_numbers)
965 show_case_numbers = !!show_case_numbers;
966 if (show_case_numbers != ds->show_case_numbers)
968 PsppSheetViewColumn *column;
970 ds->show_case_numbers = show_case_numbers;
971 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
973 pspp_sheet_view_column_set_visible (column, show_case_numbers);
975 g_object_notify (G_OBJECT (ds), "case-numbers");
976 gtk_widget_queue_draw (GTK_WIDGET (ds));
981 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
983 return data_sheet->may_create_vars;
987 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
988 gboolean may_create_vars)
990 if (data_sheet->may_create_vars != may_create_vars)
992 data_sheet->may_create_vars = may_create_vars;
993 if (data_sheet->new_variable_column)
994 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
997 on_selection_changed (pspp_sheet_view_get_selection (
998 PSPP_SHEET_VIEW (data_sheet)), NULL);
1003 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
1005 return data_sheet->may_delete_vars;
1009 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
1010 gboolean may_delete_vars)
1012 if (data_sheet->may_delete_vars != may_delete_vars)
1014 data_sheet->may_delete_vars = may_delete_vars;
1015 on_selection_changed (pspp_sheet_view_get_selection (
1016 PSPP_SHEET_VIEW (data_sheet)), NULL);
1020 static PsppSheetViewColumn *
1021 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1024 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1025 PsppireDataStore *data_store;
1026 PsppSheetViewColumn *column;
1027 struct variable *var;
1030 data_store = psppire_data_sheet_get_data_store (data_sheet);
1031 g_return_val_if_fail (data_store != NULL, NULL);
1032 g_return_val_if_fail (data_store->dict != NULL, NULL);
1034 var = psppire_dict_get_variable (data_store->dict, dict_index);
1035 g_return_val_if_fail (var != NULL, NULL);
1038 list = pspp_sheet_view_get_columns (sheet_view);
1039 for (iter = list; iter != NULL; iter = iter->next)
1041 PsppSheetViewColumn *c = iter->data;
1044 v = g_object_get_data (G_OBJECT (c), "variable");
1057 psppire_data_sheet_goto_variable (PsppireDataSheet *data_sheet,
1060 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1061 PsppSheetViewColumn *column;
1063 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1069 gint row = psppire_data_sheet_get_current_case (data_sheet);
1070 path = gtk_tree_path_new_from_indices (row >= 0 ? row : 0, -1);
1072 pspp_sheet_view_scroll_to_cell (sheet_view, path, column,
1074 pspp_sheet_view_set_cursor (sheet_view, path, column, FALSE);
1075 gtk_tree_path_free (path);
1080 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1082 PsppSheetSelection *selection;
1083 struct variable *var;
1084 GList *selected_columns;
1087 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1088 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1091 for (iter = selected_columns; iter != NULL; iter = iter->next)
1093 PsppSheetViewColumn *column = iter->data;
1094 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1107 g_list_free (selected_columns);
1113 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1115 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1116 PsppireDataStore *store = data_sheet->data_store;
1117 PsppSheetSelection *selection;
1120 g_return_if_fail (case_index >= 0);
1121 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1123 path = gtk_tree_path_new_from_indices (case_index, -1);
1125 /* Select the case. */
1126 selection = pspp_sheet_view_get_selection (sheet_view);
1127 pspp_sheet_selection_unselect_all (selection);
1128 pspp_sheet_selection_select_path (selection, path);
1129 pspp_sheet_selection_select_all_columns (selection);
1131 /* Scroll so that the case is visible. */
1132 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1134 gtk_tree_path_free (path);
1137 /* Returns the 0-based index of a selected case, if there is at least one, and
1140 If more than one case is selected, returns the one with the smallest index,
1141 that is, the index of the case closest to the beginning of the file. The
1142 row that can be used to insert a new case is not considered a case. */
1144 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1146 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1147 PsppireDataStore *store = data_sheet->data_store;
1148 const struct range_set_node *node;
1149 PsppSheetSelection *selection;
1150 struct range_set *rows;
1153 selection = pspp_sheet_view_get_selection (sheet_view);
1154 rows = pspp_sheet_selection_get_range_set (selection);
1155 node = range_set_first (rows);
1156 row = (node && node->start < psppire_data_store_get_case_count (store)
1159 range_set_destroy (rows);
1164 /* Returns the 0-based index of a selected case, if exactly one case is
1165 selected, and -1 otherwise. Returns -1 if the row that can be used to
1166 insert a new case is selected. */
1168 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1170 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1171 PsppireDataStore *store = data_sheet->data_store;
1172 const struct range_set_node *node;
1173 PsppSheetSelection *selection;
1174 struct range_set *rows;
1177 selection = pspp_sheet_view_get_selection (sheet_view);
1178 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1181 rows = pspp_sheet_selection_get_range_set (selection);
1182 node = range_set_first (rows);
1183 row = (node && node->start < psppire_data_store_get_case_count (store)
1186 range_set_destroy (rows);
1192 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1194 if (data_sheet->uim == NULL)
1197 GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1199 GTK_TYPE_UI_MANAGER));
1200 g_object_ref (data_sheet->uim);
1203 return data_sheet->uim;
1207 psppire_data_sheet_dispose (GObject *object)
1209 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1211 if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0)
1213 g_signal_handler_disconnect (data_sheet->clip,
1214 data_sheet->on_owner_change_signal);
1215 data_sheet->on_owner_change_signal = 0;
1218 if (data_sheet->dispose_has_run)
1221 data_sheet->dispose_has_run = TRUE;
1223 psppire_data_sheet_unset_data_store (data_sheet);
1225 g_object_unref (data_sheet->builder);
1227 if (data_sheet->uim)
1228 g_object_unref (data_sheet->uim);
1230 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1234 psppire_data_sheet_map (GtkWidget *widget)
1236 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1238 GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
1240 data_sheet->clip = gtk_widget_get_clipboard (widget,
1241 GDK_SELECTION_CLIPBOARD);
1242 if (data_sheet->on_owner_change_signal)
1243 g_signal_handler_disconnect (data_sheet->clip,
1244 data_sheet->on_owner_change_signal);
1245 data_sheet->on_owner_change_signal
1246 = g_signal_connect (data_sheet->clip, "owner-change",
1247 G_CALLBACK (on_owner_change), widget);
1248 on_owner_change (data_sheet->clip, NULL, widget);
1252 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1254 GObjectClass *gobject_class;
1255 GtkWidgetClass *widget_class;
1257 gobject_class = G_OBJECT_CLASS (class);
1258 gobject_class->set_property = psppire_data_sheet_set_property;
1259 gobject_class->get_property = psppire_data_sheet_get_property;
1260 gobject_class->dispose = psppire_data_sheet_dispose;
1262 widget_class = GTK_WIDGET_CLASS (class);
1263 widget_class->map = psppire_data_sheet_map;
1265 g_signal_new ("var-double-clicked",
1266 G_OBJECT_CLASS_TYPE (gobject_class),
1269 g_signal_accumulator_true_handled, NULL,
1270 psppire_marshal_BOOLEAN__INT,
1271 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1273 g_object_class_install_property (
1274 gobject_class, PROP_DATA_STORE,
1275 g_param_spec_object ("data-store",
1277 "The data store for the data sheet to display.",
1278 PSPPIRE_TYPE_DATA_STORE,
1279 G_PARAM_WRITABLE | G_PARAM_READABLE));
1281 g_object_class_install_property (
1282 gobject_class, PROP_VALUE_LABELS,
1283 g_param_spec_boolean ("value-labels",
1285 "Whether or not the data sheet should display labels instead of values",
1287 G_PARAM_WRITABLE | G_PARAM_READABLE));
1289 g_object_class_install_property (
1290 gobject_class, PROP_CASE_NUMBERS,
1291 g_param_spec_boolean ("case-numbers",
1293 "Whether or not the data sheet should display case numbers",
1295 G_PARAM_WRITABLE | G_PARAM_READABLE));
1297 g_object_class_install_property (
1300 g_param_spec_long ("current-case",
1302 "Zero based number of the selected case",
1305 G_PARAM_WRITABLE | G_PARAM_READABLE));
1307 g_object_class_install_property (
1309 PROP_MAY_CREATE_VARS,
1310 g_param_spec_boolean ("may-create-vars",
1311 "May create variables",
1312 "Whether the user may create more variables",
1314 G_PARAM_READWRITE));
1316 g_object_class_install_property (
1318 PROP_MAY_DELETE_VARS,
1319 g_param_spec_boolean ("may-delete-vars",
1320 "May delete variables",
1321 "Whether the user may delete variables",
1323 G_PARAM_READWRITE));
1325 g_object_class_install_property (
1328 g_param_spec_object ("ui-manager",
1330 "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.",
1331 GTK_TYPE_UI_MANAGER,
1336 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1338 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1341 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1342 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1346 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1348 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1352 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1353 gpointer user_data UNUSED)
1355 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1357 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1359 PsppSheetSelection *selection;
1361 selection = pspp_sheet_view_get_selection (sheet_view);
1362 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1366 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1367 &path, NULL, NULL, NULL))
1369 pspp_sheet_selection_unselect_all (selection);
1370 pspp_sheet_selection_select_path (selection, path);
1371 pspp_sheet_selection_select_all_columns (selection);
1372 gtk_tree_path_free (path);
1376 do_popup_menu (widget, event->button, event->time);
1385 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1387 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1388 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1389 const struct range_set_node *node;
1390 struct range_set *selected;
1392 selected = pspp_sheet_selection_get_range_set (selection);
1393 for (node = range_set_last (selected); node != NULL;
1394 node = range_set_prev (selected, node))
1396 unsigned long int start = range_set_node_get_start (node);
1397 unsigned long int count = range_set_node_get_width (node);
1399 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1401 range_set_destroy (selected);
1405 on_selection_changed (PsppSheetSelection *selection,
1406 gpointer user_data UNUSED)
1408 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1409 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1410 gint n_selected_rows;
1411 gboolean any_variables_selected;
1412 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1417 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1419 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1420 gtk_action_set_sensitive (action, n_selected_rows > 0);
1422 switch (n_selected_rows)
1425 may_delete_cases = FALSE;
1429 /* The row used for inserting new cases cannot be deleted. */
1430 path = gtk_tree_path_new_from_indices (
1431 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1432 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1434 gtk_tree_path_free (path);
1438 may_delete_cases = TRUE;
1441 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1442 gtk_action_set_sensitive (action, may_delete_cases);
1444 any_variables_selected = FALSE;
1445 may_delete_vars = may_insert_vars = FALSE;
1446 list = pspp_sheet_selection_get_selected_columns (selection);
1447 for (iter = list; iter != NULL; iter = iter->next)
1449 PsppSheetViewColumn *column = iter->data;
1450 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1454 may_delete_vars = may_insert_vars = TRUE;
1455 any_variables_selected = TRUE;
1458 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1459 may_insert_vars = TRUE;
1463 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1464 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1466 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1467 gtk_action_set_sensitive (action, may_insert_vars);
1469 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1470 gtk_action_set_sensitive (action, may_delete_vars);
1472 action = get_action_assert (data_sheet->builder, "sort-up");
1473 gtk_action_set_sensitive (action, may_delete_vars);
1475 action = get_action_assert (data_sheet->builder, "sort-down");
1476 gtk_action_set_sensitive (action, may_delete_vars);
1478 psppire_data_sheet_update_clip_actions (data_sheet);
1479 psppire_data_sheet_update_primary_selection (data_sheet,
1480 (n_selected_rows > 0
1481 && any_variables_selected));
1485 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1486 struct range_set **rowsp,
1487 struct range_set **colsp)
1489 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1490 PsppireDataStore *data_store = data_sheet->data_store;
1491 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1492 unsigned long n_cases;
1493 struct range_set *rows, *cols;
1496 if (data_store == NULL)
1498 n_cases = psppire_data_store_get_case_count (data_store);
1500 rows = pspp_sheet_selection_get_range_set (selection);
1501 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1502 if (range_set_is_empty (rows))
1504 range_set_destroy (rows);
1508 cols = range_set_create ();
1509 list = pspp_sheet_selection_get_selected_columns (selection);
1510 for (iter = list; iter != NULL; iter = iter->next)
1512 PsppSheetViewColumn *column = iter->data;
1513 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1516 range_set_set1 (cols, var_get_dict_index (var), 1);
1519 if (range_set_is_empty (cols))
1521 range_set_destroy (rows);
1522 range_set_destroy (cols);
1532 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1534 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1535 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1536 PsppireDataStore *data_store = data_sheet->data_store;
1537 struct range_set *selected;
1540 selected = pspp_sheet_selection_get_range_set (selection);
1541 row = range_set_scan (selected, 0);
1542 range_set_destroy (selected);
1544 if (row <= psppire_data_store_get_case_count (data_store))
1545 psppire_data_store_insert_new_case (data_store, row);
1549 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1551 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1552 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1553 PsppireDict *dict = data_sheet->data_store->dict;
1554 PsppSheetViewColumn *column;
1555 struct variable *var;
1560 list = pspp_sheet_selection_get_selected_columns (selection);
1563 column = list->data;
1566 var = g_object_get_data (G_OBJECT (column), "variable");
1567 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1568 if (psppire_dict_generate_name (dict, name, sizeof name))
1569 psppire_dict_insert_variable (dict, index, name);
1573 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1575 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1576 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1577 PsppireDict *dict = data_sheet->data_store->dict;
1580 list = pspp_sheet_selection_get_selected_columns (selection);
1583 list = g_list_reverse (list);
1584 for (iter = list; iter; iter = iter->next)
1586 PsppSheetViewColumn *column = iter->data;
1587 struct variable *var;
1589 var = g_object_get_data (G_OBJECT (column), "variable");
1591 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1603 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1605 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1606 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1607 PsppireDataWindow *pdw;
1612 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1613 g_return_if_fail (pdw != NULL);
1615 list = pspp_sheet_selection_get_selected_columns (selection);
1617 syntax = g_string_new ("SORT CASES BY");
1619 for (iter = list; iter; iter = iter->next)
1621 PsppSheetViewColumn *column = iter->data;
1622 struct variable *var;
1624 var = g_object_get_data (G_OBJECT (column), "variable");
1627 g_string_append_printf (syntax, " %s", var_get_name (var));
1633 if (order == SORT_DESCEND)
1634 g_string_append (syntax, " (DOWN)");
1635 g_string_append_c (syntax, '.');
1636 execute_const_syntax_string (pdw, syntax->str);
1638 g_string_free (syntax, TRUE);
1642 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1644 do_sort (data_sheet, SORT_ASCEND);
1648 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1650 do_sort (data_sheet, SORT_DESCEND);
1654 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1656 goto_case_dialog (data_sheet);
1660 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1662 PsppireDataWindow *pdw;
1664 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1665 g_return_if_fail (pdw != NULL);
1671 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1673 psppire_data_sheet_set_clip (data_sheet, FALSE);
1677 on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet)
1679 psppire_data_sheet_set_clip (data_sheet, TRUE);
1683 on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet)
1685 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
1686 GtkClipboard *clipboard =
1687 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1689 gtk_clipboard_request_contents (clipboard,
1690 gdk_atom_intern ("UTF8_STRING", TRUE),
1691 psppire_data_sheet_clip_received_cb,
1696 psppire_data_sheet_init (PsppireDataSheet *obj)
1698 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1701 obj->show_value_labels = FALSE;
1702 obj->show_case_numbers = TRUE;
1703 obj->may_create_vars = TRUE;
1704 obj->may_delete_vars = TRUE;
1706 obj->owns_primary_selection = FALSE;
1708 obj->scroll_to_bottom_signal = 0;
1709 obj->scroll_to_right_signal = 0;
1710 obj->on_owner_change_signal = 0;
1711 obj->new_variable_column = NULL;
1712 obj->container = NULL;
1715 obj->dispose_has_run = FALSE;
1717 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1719 g_signal_connect (obj, "notify::model",
1720 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1722 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1723 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1724 PSPP_SHEET_SELECTION_RECTANGLE);
1726 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1727 g_signal_connect (obj, "query-tooltip",
1728 G_CALLBACK (on_query_tooltip), NULL);
1729 g_signal_connect (obj, "button-press-event",
1730 G_CALLBACK (on_button_pressed), NULL);
1731 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1733 obj->builder = builder_new ("data-sheet.ui");
1735 action = get_action_assert (obj->builder, "edit_clear-cases");
1736 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1738 gtk_action_set_sensitive (action, FALSE);
1739 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1740 "changed", G_CALLBACK (on_selection_changed), NULL);
1742 action = get_action_assert (obj->builder, "edit_insert-case");
1743 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1746 action = get_action_assert (obj->builder, "edit_insert-variable");
1747 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1750 action = get_action_assert (obj->builder, "edit_goto-case");
1751 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1754 action = get_action_assert (obj->builder, "edit_copy");
1755 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1757 action = get_action_assert (obj->builder, "edit_cut");
1758 g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj);
1760 action = get_action_assert (obj->builder, "edit_paste");
1761 g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj);
1763 action = get_action_assert (obj->builder, "edit_clear-variables");
1764 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1767 action = get_action_assert (obj->builder, "edit_find");
1768 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1770 action = get_action_assert (obj->builder, "sort-up");
1771 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1773 action = get_action_assert (obj->builder, "sort-down");
1774 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1779 psppire_data_sheet_new (void)
1781 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1785 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1787 return data_sheet->data_store;
1791 refresh_model (PsppireDataSheet *data_sheet)
1793 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1795 if (data_sheet->data_store != NULL)
1797 PsppireEmptyListStore *model;
1801 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1802 model = psppire_empty_list_store_new (n_rows);
1803 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1804 GTK_TREE_MODEL (model));
1805 g_object_unref (model);
1807 action = get_action_assert (data_sheet->builder, "edit_copy");
1808 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1814 on_case_inserted (PsppireDataStore *data_store, gint row,
1815 PsppireDataSheet *data_sheet)
1817 PsppireEmptyListStore *empty_list_store;
1818 GtkTreeModel *tree_model;
1821 g_return_if_fail (data_store == data_sheet->data_store);
1823 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1824 if (row == n_rows - 1)
1827 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1828 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1829 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1830 psppire_empty_list_store_row_inserted (empty_list_store, row);
1834 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1835 PsppireDataSheet *data_sheet)
1838 g_return_if_fail (data_store == data_sheet->data_store);
1842 /* This is a bit of a cop-out. We could do better, if it ever turns out
1843 that this performs too poorly. */
1844 refresh_model (data_sheet);
1848 PsppireEmptyListStore *empty_list_store;
1849 GtkTreeModel *tree_model;
1850 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1852 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1853 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1854 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1855 psppire_empty_list_store_row_deleted (empty_list_store, first);
1860 on_case_change (PsppireDataStore *data_store, gint row,
1861 PsppireDataSheet *data_sheet)
1863 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1865 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1866 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1870 on_backend_changed (PsppireDataStore *data_store,
1871 PsppireDataSheet *data_sheet)
1873 g_return_if_fail (data_store == data_sheet->data_store);
1874 refresh_model (data_sheet);
1878 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1879 PsppireDataSheet *data_sheet)
1881 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1882 PsppSheetViewColumn *column;
1883 struct variable *var;
1887 g_return_if_fail (data_sheet->data_store != NULL);
1888 g_return_if_fail (dict == data_sheet->data_store->dict);
1890 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1895 var = psppire_dict_get_variable (data_store->dict, dict_index);
1896 g_return_if_fail (var != NULL);
1898 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1899 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1900 if (display_width != var_get_display_width (var))
1902 gint base_width, incr_width;
1904 display_width = var_get_display_width (var);
1905 calc_width_conversion (data_sheet, &base_width, &incr_width);
1906 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1907 base_width, incr_width);
1908 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1913 on_variable_changed (PsppireDict *dict, int dict_index,
1914 guint what, const struct variable *oldvar,
1915 PsppireDataSheet *data_sheet)
1917 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1918 PsppSheetViewColumn *column;
1919 GtkCellRenderer *cell;
1920 struct variable *var;
1924 g_return_if_fail (data_sheet->data_store != NULL);
1925 g_return_if_fail (dict == data_sheet->data_store->dict);
1928 if (what & VAR_TRAIT_DISPLAY_WIDTH)
1929 on_variable_display_width_changed (dict, dict_index, data_sheet);
1931 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1937 var = psppire_dict_get_variable (data_store->dict, dict_index);
1938 g_return_if_fail (var != NULL);
1940 name = escape_underscores (var_get_name (var));
1941 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1942 pspp_sheet_view_column_set_title (column, name);
1945 cells = pspp_sheet_view_column_get_cell_renderers (column);
1946 g_return_if_fail (cells);
1948 g_list_free (cells);
1950 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1952 /* Stop editing before we delete and replace the cell renderers.
1953 Otherwise if this column is currently being edited, an eventual call
1954 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1955 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1957 It's possible that this is a bug in PsppSheetView, and it's possible
1958 that PsppSheetView inherits that from GtkTreeView, but I haven't
1959 investigated yet. */
1960 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1962 add_data_column_cell_renderer (data_sheet, column);
1967 on_variable_inserted (PsppireDict *dict, int var_index,
1968 PsppireDataSheet *data_sheet)
1970 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1971 gint base_width, incr_width;
1972 PsppSheetViewColumn *column;
1974 calc_width_conversion (data_sheet, &base_width, &incr_width);
1975 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1976 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1980 on_variable_deleted (PsppireDict *dict,
1981 const struct variable *var, int case_idx, int width,
1982 PsppireDataSheet *data_sheet)
1984 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1985 GList *columns, *iter;
1987 columns = pspp_sheet_view_get_columns (sheet_view);
1988 for (iter = columns; iter != NULL; iter = iter->next)
1990 PsppSheetViewColumn *column = iter->data;
1991 const struct variable *column_var;
1993 column_var = g_object_get_data (G_OBJECT (column), "variable");
1994 if (column_var == var)
1995 pspp_sheet_view_remove_column (sheet_view, column);
1997 g_list_free (columns);
2001 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
2003 PsppireDataStore *store = data_sheet->data_store;
2008 data_sheet->data_store = NULL;
2010 g_signal_handlers_disconnect_by_func (
2011 store, G_CALLBACK (on_backend_changed), data_sheet);
2012 g_signal_handlers_disconnect_by_func (
2013 store, G_CALLBACK (on_case_inserted), data_sheet);
2014 g_signal_handlers_disconnect_by_func (
2015 store, G_CALLBACK (on_cases_deleted), data_sheet);
2016 g_signal_handlers_disconnect_by_func (
2017 store, G_CALLBACK (on_case_change), data_sheet);
2019 g_signal_handlers_disconnect_by_func (
2020 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
2021 g_signal_handlers_disconnect_by_func (
2022 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
2023 g_signal_handlers_disconnect_by_func (
2024 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
2025 g_signal_handlers_disconnect_by_func (
2026 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
2028 g_object_unref (store);
2032 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
2033 PsppireDataStore *data_store)
2035 psppire_data_sheet_unset_data_store (data_sheet);
2037 data_sheet->data_store = data_store;
2038 if (data_store != NULL)
2040 g_object_ref (data_store);
2041 g_signal_connect (data_store, "backend-changed",
2042 G_CALLBACK (on_backend_changed), data_sheet);
2043 g_signal_connect (data_store, "case-inserted",
2044 G_CALLBACK (on_case_inserted), data_sheet);
2045 g_signal_connect (data_store, "cases-deleted",
2046 G_CALLBACK (on_cases_deleted), data_sheet);
2047 g_signal_connect (data_store, "case-changed",
2048 G_CALLBACK (on_case_change), data_sheet);
2050 /* XXX it's unclean to hook into the dict this way--what if the dict
2051 changes? As of this writing, though, nothing ever changes the
2052 data_store's dict. */
2053 g_signal_connect (data_store->dict, "variable-changed",
2054 G_CALLBACK (on_variable_changed),
2056 g_signal_connect (data_store->dict, "variable-inserted",
2057 G_CALLBACK (on_variable_inserted), data_sheet);
2058 g_signal_connect (data_store->dict, "variable-deleted",
2059 G_CALLBACK (on_variable_deleted), data_sheet);
2061 refresh_model (data_sheet);
2064 /* Clipboard stuff */
2066 /* A casereader and dictionary holding the data currently in the clip */
2067 static struct casereader *clip_datasheet = NULL;
2068 static struct dictionary *clip_dict = NULL;
2071 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2074 psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
2075 struct casereader **readerp,
2076 struct dictionary **dictp)
2078 struct casewriter *writer ;
2079 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2080 struct case_map *map = NULL;
2081 struct range_set *rows, *cols;
2082 const struct range_set_node *node;
2083 struct dictionary *dict;
2085 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2092 /* Construct clip dictionary. */
2093 *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
2094 RANGE_SET_FOR_EACH (node, cols)
2098 for (dict_index = node->start; dict_index < node->end; dict_index++)
2100 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2101 dict_clone_var_assert (dict, var);
2105 /* Construct clip data. */
2106 map = case_map_by_name (ds->dict->dict, dict);
2107 writer = autopaging_writer_create (dict_get_proto (dict));
2108 RANGE_SET_FOR_EACH (node, rows)
2110 unsigned long int row;
2112 for (row = node->start; row < node->end; row++)
2114 struct ccase *old = psppire_data_store_get_case (ds, row);
2116 casewriter_write (writer, case_map_execute (map, old));
2118 casewriter_force_error (writer);
2121 case_map_destroy (map);
2123 /* Clear data that we copied out, if we're doing a "cut". */
2124 if (cut && !casewriter_error (writer))
2126 RANGE_SET_FOR_EACH (node, rows)
2128 unsigned long int row;
2130 for (row = node->start; row < node->end; row++)
2132 const struct range_set_node *node2;
2134 RANGE_SET_FOR_EACH (node2, cols)
2138 for (dict_index = node2->start; dict_index < node2->end;
2141 struct variable *var;
2143 var = dict_get_var (ds->dict->dict, dict_index);
2144 psppire_data_store_set_string (ds, "", row,
2152 range_set_destroy (rows);
2153 range_set_destroy (cols);
2155 *readerp = casewriter_make_reader (writer);
2160 /* Set the clip from the currently selected range in DATA_SHEET. If CUT is
2161 true, clears the original data from DATA_SHEET, otherwise leaves the
2162 original data in-place. */
2164 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2167 struct casereader *reader;
2168 struct dictionary *dict;
2170 if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
2172 casereader_destroy (clip_datasheet);
2173 dict_destroy (clip_dict);
2175 clip_datasheet = reader;
2178 psppire_data_sheet_update_clipboard (data_sheet);
2189 /* Perform data_out for case CC, variable V, appending to STRING */
2191 data_out_g_string (GString *string, const struct variable *v,
2192 const struct ccase *cc)
2194 const struct fmt_spec *fs = var_get_print_format (v);
2195 const union value *val = case_data (cc, v);
2197 char *s = data_out (val, var_get_encoding (v), fs);
2199 g_string_append (string, s);
2205 clip_to_text (struct casereader *datasheet, struct dictionary *dict)
2210 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2211 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2212 const size_t var_cnt = dict_get_var_cnt (dict);
2214 string = g_string_sized_new (10 * val_cnt * case_cnt);
2216 for (r = 0 ; r < case_cnt ; ++r )
2221 cc = casereader_peek (datasheet, r);
2224 g_warning ("Clipboard seems to have inexplicably shrunk");
2228 for (c = 0 ; c < var_cnt ; ++c)
2230 const struct variable *v = dict_get_var (dict, c);
2231 data_out_g_string (string, v, cc);
2232 if ( c < val_cnt - 1 )
2233 g_string_append (string, "\t");
2237 g_string_append (string, "\n");
2247 clip_to_html (struct casereader *datasheet, struct dictionary *dict)
2252 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2253 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2254 const size_t var_cnt = dict_get_var_cnt (dict);
2256 /* Guestimate the size needed */
2257 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2259 g_string_append (string,
2260 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2262 g_string_append (string, "<table>\n");
2263 for (r = 0 ; r < case_cnt ; ++r )
2266 struct ccase *cc = casereader_peek (datasheet, r);
2269 g_warning ("Clipboard seems to have inexplicably shrunk");
2272 g_string_append (string, "<tr>\n");
2274 for (c = 0 ; c < var_cnt ; ++c)
2276 const struct variable *v = dict_get_var (dict, c);
2277 g_string_append (string, "<td>");
2278 data_out_g_string (string, v, cc);
2279 g_string_append (string, "</td>\n");
2282 g_string_append (string, "</tr>\n");
2286 g_string_append (string, "</table>\n");
2294 psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
2296 struct casereader *reader,
2297 struct dictionary *dict)
2299 GString *string = NULL;
2303 case SELECT_FMT_TEXT:
2304 string = clip_to_text (reader, dict);
2306 case SELECT_FMT_HTML:
2307 string = clip_to_html (reader, dict);
2310 g_assert_not_reached ();
2313 gtk_selection_data_set (selection_data, selection_data->target,
2315 (const guchar *) string->str, string->len);
2317 g_string_free (string, TRUE);
2321 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2322 GtkSelectionData *selection_data,
2326 psppire_data_sheet_clipboard_set (selection_data, info,
2327 clip_datasheet, clip_dict);
2331 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2334 dict_destroy (clip_dict);
2337 casereader_destroy (clip_datasheet);
2338 clip_datasheet = NULL;
2342 static const GtkTargetEntry targets[] = {
2343 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2344 { "STRING", 0, SELECT_FMT_TEXT },
2345 { "TEXT", 0, SELECT_FMT_TEXT },
2346 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2347 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2348 { "text/plain", 0, SELECT_FMT_TEXT },
2349 { "text/html", 0, SELECT_FMT_HTML }
2355 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2357 GtkClipboard *clipboard =
2358 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2359 GDK_SELECTION_CLIPBOARD);
2361 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2362 G_N_ELEMENTS (targets),
2363 psppire_data_sheet_clipboard_get_cb,
2364 psppire_data_sheet_clipboard_clear_cb,
2366 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2370 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2372 struct range_set *rows, *cols;
2376 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2379 range_set_destroy (rows);
2380 range_set_destroy (cols);
2383 action = get_action_assert (data_sheet->builder, "edit_copy");
2384 gtk_action_set_sensitive (action, enable);
2386 action = get_action_assert (data_sheet->builder, "edit_cut");
2387 gtk_action_set_sensitive (action, enable);
2391 psppire_data_sheet_primary_get_cb (GtkClipboard *clipboard,
2392 GtkSelectionData *selection_data,
2396 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2397 struct casereader *reader;
2398 struct dictionary *dict;
2400 if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
2402 psppire_data_sheet_clipboard_set (selection_data, info,
2404 casereader_destroy (reader);
2405 dict_destroy (dict);
2410 psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
2411 gboolean should_own)
2413 GtkClipboard *clipboard;
2414 GdkDisplay *display;
2416 display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
2417 clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
2418 g_return_if_fail (clipboard != NULL);
2420 if (data_sheet->owns_primary_selection && !should_own)
2422 data_sheet->owns_primary_selection = FALSE;
2423 gtk_clipboard_clear (clipboard);
2425 else if (should_own)
2426 data_sheet->owns_primary_selection =
2427 gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2428 psppire_data_sheet_primary_get_cb,
2429 NULL, G_OBJECT (data_sheet));
2432 /* A callback for when the clipboard contents have been received. */
2434 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2435 GtkSelectionData *sd,
2438 PsppireDataSheet *data_sheet = data;
2439 PsppireDataStore *store = data_sheet->data_store;
2440 struct range_set *rows, *cols;
2442 gint next_row, next_column;
2446 if ( sd->length < 0 )
2449 if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
2452 c = (char *) sd->data;
2454 /* Get the starting selected position in the data sheet. (Possibly we should
2455 only paste into the selected range if it's larger than one cell?) */
2456 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2458 next_row = range_set_first (rows)->start;
2459 first_column = next_column = range_set_first (cols)->start;
2460 range_set_destroy (rows);
2461 range_set_destroy (cols);
2463 g_return_if_fail (next_row >= 0);
2464 g_return_if_fail (next_column >= 0);
2466 while (count < sd->length)
2468 gint row = next_row;
2469 gint column = next_column;
2470 struct variable *var;
2473 while (*c != '\t' && *c != '\n' && count < sd->length)
2481 next_column = column + 1;
2483 else if ( *c == '\n')
2486 next_column = first_column;
2491 var = psppire_dict_get_variable (store->dict, column);
2493 psppire_data_store_set_string (store, s, row, var, FALSE);
2498 psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
2503 GtkAction *action = GTK_ACTION (data);
2504 gboolean compatible_target;
2507 compatible_target = FALSE;
2508 for (i = 0; i < G_N_ELEMENTS (targets); i++)
2510 GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
2513 for (j = 0; j < n_atoms; j++)
2514 if (target == atoms[j])
2516 compatible_target = TRUE;
2521 gtk_action_set_sensitive (action, compatible_target);
2522 g_object_unref (action);
2526 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2528 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2529 GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste");
2531 g_object_ref (action);
2532 gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb,