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_preferred_width (renderer, GTK_WIDGET (treeview),
301 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
308 ds_put_byte_multiple (&s, '0', char_cnt);
309 ds_put_byte (&s, ' ');
310 width = get_string_width (treeview, renderer, ds_cstr (&s));
317 on_data_column_editing_started (GtkCellRenderer *cell,
318 GtkCellEditable *editable,
322 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
323 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
324 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
325 struct variable *var;
327 g_return_if_fail (column);
328 g_return_if_fail (data_sheet);
329 g_return_if_fail (data_store);
332 g_object_ref (editable);
333 g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable",
334 editable, g_object_unref);
336 var = g_object_get_data (G_OBJECT (column), "variable");
337 g_return_if_fail (var);
339 if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable))
341 const struct val_labs *labels = var_get_value_labels (var);
342 const struct val_lab **vls = val_labs_sorted (labels);
343 size_t n_vls = val_labs_count (labels);
344 GtkListStore *list_store;
347 list_store = gtk_list_store_new (1, G_TYPE_STRING);
348 for (i = 0; i < n_vls; ++i)
350 const struct val_lab *vl = vls[i];
353 gtk_list_store_append (list_store, &iter);
354 gtk_list_store_set (list_store, &iter,
355 0, val_lab_get_label (vl),
360 gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
361 GTK_TREE_MODEL (list_store));
362 g_object_unref (list_store);
367 scroll_to_bottom (GtkWidget *widget,
368 GtkRequisition *requisition,
369 gpointer unused UNUSED)
371 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
372 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
373 GtkAdjustment *vadjust;
375 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
376 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
378 if (data_sheet->scroll_to_bottom_signal)
380 g_signal_handler_disconnect (data_sheet,
381 data_sheet->scroll_to_bottom_signal);
382 data_sheet->scroll_to_bottom_signal = 0;
387 on_data_column_edited (GtkCellRendererText *cell,
392 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
393 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
394 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
395 GtkEditable *editable;
396 struct variable *var;
402 path = gtk_tree_path_new_from_string (path_string);
403 row = gtk_tree_path_get_indices (path)[0];
404 gtk_tree_path_free (path);
406 var = g_object_get_data (G_OBJECT (column), "variable");
408 new_row = row == psppire_data_store_get_case_count (data_store);
409 if (new_row && new_text[0] == '\0')
412 editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
413 g_return_if_fail (editable != NULL);
414 is_val_lab = (GTK_IS_COMBO_BOX (editable)
415 && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
416 g_object_unref (editable);
418 psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
420 if (new_row && !data_sheet->scroll_to_bottom_signal)
422 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
423 data_sheet->scroll_to_bottom_signal =
424 g_signal_connect (data_sheet, "size-allocate",
425 G_CALLBACK (scroll_to_bottom), NULL);
429 /* We could be more specific about what to redraw, if it seems
430 important for performance. */
431 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
436 scroll_to_right (GtkWidget *widget,
437 PsppireDataSheet *data_sheet)
439 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
440 PsppSheetViewColumn *column, *prev;
441 GList *columns, *iter;
445 columns = pspp_sheet_view_get_columns (sheet_view);
446 for (iter = columns; iter; iter = iter->next)
448 PsppSheetViewColumn *c = iter->data;
449 if (g_object_get_data (G_OBJECT (c), "new-var-column"))
456 g_list_free (columns);
461 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
467 pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
470 pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
471 gtk_tree_path_free (path);
475 if (data_sheet->scroll_to_right_signal)
477 g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
478 data_sheet->scroll_to_right_signal = 0;
483 on_new_variable_column_edited (GtkCellRendererText *cell,
488 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
489 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
490 PsppireDict *dict = data_store->dict;
491 struct variable *var;
496 if (new_text[0] == '\0')
498 /* User didn't enter anything so don't create a variable. */
502 path = gtk_tree_path_new_from_string (path_string);
503 row = gtk_tree_path_get_indices (path)[0];
504 gtk_tree_path_free (path);
506 if (!psppire_dict_generate_name (dict, name, sizeof name))
509 var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
511 g_return_if_fail (var != NULL);
513 psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
515 if (!data_sheet->scroll_to_right_signal)
517 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
518 data_sheet->scroll_to_right_signal =
519 g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
520 G_CALLBACK (scroll_to_right), data_sheet);
524 /* We could be more specific about what to redraw, if it seems
525 important for performance. */
526 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
531 calc_width_conversion (PsppireDataSheet *data_sheet,
532 gint *base_width, gint *incr_width)
534 GtkCellRenderer *cell;
537 cell = gtk_cell_renderer_text_new ();
538 w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
539 w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
540 *incr_width = MAX (1, (w10 - w1) / 9);
541 *base_width = MAX (0, w10 - *incr_width * 10);
542 g_object_ref_sink (cell);
543 g_object_unref (cell);
547 display_width_from_pixel_width (PsppireDataSheet *data_sheet,
550 gint base_width, incr_width;
552 calc_width_conversion (data_sheet, &base_width, &incr_width);
553 return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
557 display_width_to_pixel_width (PsppireDataSheet *data_sheet,
562 return base_width + incr_width * display_width;
566 on_data_column_resized (GObject *gobject,
570 PsppireDataSheet *data_sheet = user_data;
571 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
572 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
573 struct variable *var;
577 if (data_store == NULL)
580 pixel_width = pspp_sheet_view_column_get_width (column);
581 if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
583 /* Short-circuit the expensive display_width_from_pixel_width()
584 calculation, to make loading .sav files with 2000 columns visibly
589 var = g_object_get_data (G_OBJECT (column), "variable");
590 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
591 var_set_display_width (var, display_width);
595 do_data_column_popup_menu (PsppSheetViewColumn *column,
596 guint button, guint32 time)
598 GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
599 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
602 menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
603 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
607 on_data_column_popup_menu (PsppSheetViewColumn *column,
608 gpointer user_data UNUSED)
610 do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
614 on_column_button_press_event (PsppSheetViewColumn *column,
615 GdkEventButton *event,
616 gpointer user_data UNUSED)
618 PsppSheetSelection *selection;
619 PsppSheetView *sheet_view;
621 sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
623 g_return_val_if_fail (sheet_view != NULL, FALSE);
625 selection = pspp_sheet_view_get_selection (sheet_view);
626 g_return_val_if_fail (selection != NULL, FALSE);
628 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
630 do_data_column_popup_menu (column, event->button, event->time);
633 else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
635 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
636 struct variable *var;
638 var = g_object_get_data (G_OBJECT (column), "variable");
643 g_signal_emit_by_name (data_sheet, "var-double-clicked",
644 var_get_dict_index (var), &handled);
653 on_data_column_query_tooltip (PsppSheetViewColumn *column,
655 gpointer user_data UNUSED)
657 struct variable *var;
660 var = g_object_get_data (G_OBJECT (column), "variable");
661 g_return_val_if_fail (var != NULL, FALSE);
663 text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
664 gtk_tooltip_set_text (tooltip, text);
670 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
671 PsppSheetViewColumn *column)
673 GtkCellRenderer *cell;
674 struct variable *var;
676 var = g_object_get_data (G_OBJECT (column), "variable");
677 g_return_if_fail (var != NULL);
679 if (data_sheet->show_value_labels && var_has_value_labels (var))
681 cell = gtk_cell_renderer_combo_new ();
682 g_object_set (G_OBJECT (cell),
688 cell = gtk_cell_renderer_text_new ();
690 g_signal_connect (cell, "editing-started",
691 G_CALLBACK (on_data_column_editing_started), NULL);
692 g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
694 g_object_set_data (G_OBJECT (cell), "column", column);
695 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
697 pspp_sheet_view_column_clear (column);
698 pspp_sheet_view_column_pack_start (column, cell, TRUE);
700 pspp_sheet_view_column_set_cell_data_func (
701 column, cell, render_data_cell, data_sheet, NULL);
704 static PsppSheetViewColumn *
705 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
706 gint base_width, gint incr_width)
708 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
709 struct variable *var;
710 PsppSheetViewColumn *column;
714 var = psppire_dict_get_variable (data_store->dict, dict_idx);
716 column = pspp_sheet_view_column_new ();
718 name = escape_underscores (var_get_name (var));
719 pspp_sheet_view_column_set_title (column, name);
722 g_object_set_data (G_OBJECT (column), "variable", var);
724 width = display_width_to_pixel_width (data_sheet,
725 var_get_display_width (var),
726 base_width, incr_width);
727 pspp_sheet_view_column_set_min_width (column, 10);
728 pspp_sheet_view_column_set_fixed_width (column, width);
729 pspp_sheet_view_column_set_resizable (column, TRUE);
731 pspp_sheet_view_column_set_clickable (column, TRUE);
732 g_signal_connect (column, "notify::width",
733 G_CALLBACK (on_data_column_resized), data_sheet);
735 g_signal_connect (column, "button-press-event",
736 G_CALLBACK (on_column_button_press_event),
738 g_signal_connect (column, "query-tooltip",
739 G_CALLBACK (on_data_column_query_tooltip), NULL);
740 g_signal_connect (column, "popup-menu",
741 G_CALLBACK (on_data_column_popup_menu), data_sheet);
743 add_data_column_cell_renderer (data_sheet, column);
749 make_new_variable_column (PsppireDataSheet *data_sheet,
750 gint base_width, gint incr_width)
752 PsppSheetViewColumn *column;
753 GtkCellRenderer *cell;
756 cell = gtk_cell_renderer_text_new ();
757 g_object_set (cell, "editable", TRUE, NULL);
759 g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
762 column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
763 g_object_set_data (G_OBJECT (column), "new-var-column", column);
765 width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
766 pspp_sheet_view_column_set_min_width (column, 10);
767 pspp_sheet_view_column_set_fixed_width (column, width);
768 pspp_sheet_view_column_set_tabbable (column, FALSE);
770 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
771 g_signal_connect (column, "button-press-event",
772 G_CALLBACK (on_column_button_press_event),
774 g_signal_connect (column, "popup-menu",
775 G_CALLBACK (on_data_column_popup_menu), data_sheet);
777 pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
779 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
780 data_sheet->new_variable_column = column;
784 psppire_data_sheet_model_changed (GObject *gobject,
788 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
789 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
790 PsppireDataStore *data_store;
792 /* Remove old columns. */
795 PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
799 pspp_sheet_view_remove_column (sheet_view, column);
801 data_sheet->new_variable_column = NULL;
803 if (pspp_sheet_view_get_model (sheet_view) == NULL)
805 /* Don't create any columns at all if there's no model. Otherwise we'll
806 create some columns as part of the "dispose" callback for the sheet
807 view, which sets the model to NULL. That causes warnings to be
808 logged and is obviously undesirable in any case. */
812 /* Add new columns. */
813 data_store = psppire_data_sheet_get_data_store (data_sheet);
814 if (data_store != NULL)
816 gint base_width, incr_width;
819 calc_width_conversion (data_sheet, &base_width, &incr_width);
821 make_row_number_column (data_sheet, data_store);
822 for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
824 PsppSheetViewColumn *column;
826 column = make_data_column (data_sheet, i, base_width, incr_width);
827 pspp_sheet_view_append_column (sheet_view, column);
829 make_new_variable_column (data_sheet, base_width, incr_width);
840 PROP_MAY_CREATE_VARS,
841 PROP_MAY_DELETE_VARS,
846 psppire_data_sheet_set_property (GObject *object,
851 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
855 case PROP_DATA_STORE:
856 psppire_data_sheet_set_data_store (
857 obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
860 case PROP_VALUE_LABELS:
861 psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
864 case PROP_CASE_NUMBERS:
865 psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
868 case PROP_CURRENT_CASE:
869 psppire_data_sheet_goto_case (obj, g_value_get_long (value));
872 case PROP_MAY_CREATE_VARS:
873 psppire_data_sheet_set_may_create_vars (obj,
874 g_value_get_boolean (value));
877 case PROP_MAY_DELETE_VARS:
878 psppire_data_sheet_set_may_delete_vars (obj,
879 g_value_get_boolean (value));
882 case PROP_UI_MANAGER:
884 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
890 psppire_data_sheet_get_property (GObject *object,
895 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
899 case PROP_DATA_STORE:
900 g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
903 case PROP_VALUE_LABELS:
904 g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
907 case PROP_CASE_NUMBERS:
908 g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
911 case PROP_CURRENT_CASE:
912 g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
915 case PROP_MAY_CREATE_VARS:
916 g_value_set_boolean (value, obj->may_create_vars);
919 case PROP_MAY_DELETE_VARS:
920 g_value_set_boolean (value, obj->may_delete_vars);
923 case PROP_UI_MANAGER:
924 g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
928 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
934 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
936 return ds->show_value_labels;
940 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
941 gboolean show_value_labels)
943 show_value_labels = !!show_value_labels;
944 if (show_value_labels != ds->show_value_labels)
946 ds->show_value_labels = show_value_labels;
947 g_object_notify (G_OBJECT (ds), "value-labels");
949 /* Pretend the model changed, to force the columns to be rebuilt.
950 Otherwise cell renderers won't get changed from combo boxes to text
951 entries or vice versa. */
952 g_object_notify (G_OBJECT (ds), "model");
957 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
959 return ds->show_case_numbers;
963 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
964 gboolean show_case_numbers)
966 show_case_numbers = !!show_case_numbers;
967 if (show_case_numbers != ds->show_case_numbers)
969 PsppSheetViewColumn *column;
971 ds->show_case_numbers = show_case_numbers;
972 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
974 pspp_sheet_view_column_set_visible (column, show_case_numbers);
976 g_object_notify (G_OBJECT (ds), "case-numbers");
977 gtk_widget_queue_draw (GTK_WIDGET (ds));
982 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
984 return data_sheet->may_create_vars;
988 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
989 gboolean may_create_vars)
991 if (data_sheet->may_create_vars != may_create_vars)
993 data_sheet->may_create_vars = may_create_vars;
994 if (data_sheet->new_variable_column)
995 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
998 on_selection_changed (pspp_sheet_view_get_selection (
999 PSPP_SHEET_VIEW (data_sheet)), NULL);
1004 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
1006 return data_sheet->may_delete_vars;
1010 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
1011 gboolean may_delete_vars)
1013 if (data_sheet->may_delete_vars != may_delete_vars)
1015 data_sheet->may_delete_vars = may_delete_vars;
1016 on_selection_changed (pspp_sheet_view_get_selection (
1017 PSPP_SHEET_VIEW (data_sheet)), NULL);
1021 static PsppSheetViewColumn *
1022 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1025 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1026 PsppireDataStore *data_store;
1027 PsppSheetViewColumn *column;
1028 struct variable *var;
1031 data_store = psppire_data_sheet_get_data_store (data_sheet);
1032 g_return_val_if_fail (data_store != NULL, NULL);
1033 g_return_val_if_fail (data_store->dict != NULL, NULL);
1035 var = psppire_dict_get_variable (data_store->dict, dict_index);
1036 g_return_val_if_fail (var != NULL, NULL);
1039 list = pspp_sheet_view_get_columns (sheet_view);
1040 for (iter = list; iter != NULL; iter = iter->next)
1042 PsppSheetViewColumn *c = iter->data;
1045 v = g_object_get_data (G_OBJECT (c), "variable");
1058 psppire_data_sheet_goto_variable (PsppireDataSheet *data_sheet,
1061 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1062 PsppSheetViewColumn *column;
1064 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1070 gint row = psppire_data_sheet_get_current_case (data_sheet);
1071 path = gtk_tree_path_new_from_indices (row >= 0 ? row : 0, -1);
1073 pspp_sheet_view_scroll_to_cell (sheet_view, path, column,
1075 pspp_sheet_view_set_cursor (sheet_view, path, column, FALSE);
1076 gtk_tree_path_free (path);
1081 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1083 PsppSheetSelection *selection;
1084 struct variable *var;
1085 GList *selected_columns;
1088 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1089 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1092 for (iter = selected_columns; iter != NULL; iter = iter->next)
1094 PsppSheetViewColumn *column = iter->data;
1095 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1108 g_list_free (selected_columns);
1114 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1116 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1117 PsppireDataStore *store = data_sheet->data_store;
1118 PsppSheetSelection *selection;
1121 g_return_if_fail (case_index >= 0);
1122 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1124 path = gtk_tree_path_new_from_indices (case_index, -1);
1126 /* Select the case. */
1127 selection = pspp_sheet_view_get_selection (sheet_view);
1128 pspp_sheet_selection_unselect_all (selection);
1129 pspp_sheet_selection_select_path (selection, path);
1130 pspp_sheet_selection_select_all_columns (selection);
1132 /* Scroll so that the case is visible. */
1133 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1135 gtk_tree_path_free (path);
1138 /* Returns the 0-based index of a selected case, if there is at least one, and
1141 If more than one case is selected, returns the one with the smallest index,
1142 that is, the index of the case closest to the beginning of the file. The
1143 row that can be used to insert a new case is not considered a case. */
1145 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1147 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1148 PsppireDataStore *store = data_sheet->data_store;
1149 const struct range_set_node *node;
1150 PsppSheetSelection *selection;
1151 struct range_set *rows;
1154 selection = pspp_sheet_view_get_selection (sheet_view);
1155 rows = pspp_sheet_selection_get_range_set (selection);
1156 node = range_set_first (rows);
1157 row = (node && node->start < psppire_data_store_get_case_count (store)
1160 range_set_destroy (rows);
1165 /* Returns the 0-based index of a selected case, if exactly one case is
1166 selected, and -1 otherwise. Returns -1 if the row that can be used to
1167 insert a new case is selected. */
1169 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1171 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1172 PsppireDataStore *store = data_sheet->data_store;
1173 const struct range_set_node *node;
1174 PsppSheetSelection *selection;
1175 struct range_set *rows;
1178 selection = pspp_sheet_view_get_selection (sheet_view);
1179 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1182 rows = pspp_sheet_selection_get_range_set (selection);
1183 node = range_set_first (rows);
1184 row = (node && node->start < psppire_data_store_get_case_count (store)
1187 range_set_destroy (rows);
1193 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1195 if (data_sheet->uim == NULL)
1198 GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1200 GTK_TYPE_UI_MANAGER));
1201 g_object_ref (data_sheet->uim);
1204 return data_sheet->uim;
1208 psppire_data_sheet_dispose (GObject *object)
1210 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1212 if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0)
1214 g_signal_handler_disconnect (data_sheet->clip,
1215 data_sheet->on_owner_change_signal);
1216 data_sheet->on_owner_change_signal = 0;
1219 if (data_sheet->dispose_has_run)
1222 data_sheet->dispose_has_run = TRUE;
1224 psppire_data_sheet_unset_data_store (data_sheet);
1226 g_object_unref (data_sheet->builder);
1228 if (data_sheet->uim)
1229 g_object_unref (data_sheet->uim);
1231 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1235 psppire_data_sheet_map (GtkWidget *widget)
1237 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1239 GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
1241 data_sheet->clip = gtk_widget_get_clipboard (widget,
1242 GDK_SELECTION_CLIPBOARD);
1243 if (data_sheet->on_owner_change_signal)
1244 g_signal_handler_disconnect (data_sheet->clip,
1245 data_sheet->on_owner_change_signal);
1246 data_sheet->on_owner_change_signal
1247 = g_signal_connect (data_sheet->clip, "owner-change",
1248 G_CALLBACK (on_owner_change), widget);
1249 on_owner_change (data_sheet->clip, NULL, widget);
1253 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1255 GObjectClass *gobject_class;
1256 GtkWidgetClass *widget_class;
1258 gobject_class = G_OBJECT_CLASS (class);
1259 gobject_class->set_property = psppire_data_sheet_set_property;
1260 gobject_class->get_property = psppire_data_sheet_get_property;
1261 gobject_class->dispose = psppire_data_sheet_dispose;
1263 widget_class = GTK_WIDGET_CLASS (class);
1264 widget_class->map = psppire_data_sheet_map;
1266 g_signal_new ("var-double-clicked",
1267 G_OBJECT_CLASS_TYPE (gobject_class),
1270 g_signal_accumulator_true_handled, NULL,
1271 psppire_marshal_BOOLEAN__INT,
1272 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1274 g_object_class_install_property (
1275 gobject_class, PROP_DATA_STORE,
1276 g_param_spec_object ("data-store",
1278 "The data store for the data sheet to display.",
1279 PSPPIRE_TYPE_DATA_STORE,
1280 G_PARAM_WRITABLE | G_PARAM_READABLE));
1282 g_object_class_install_property (
1283 gobject_class, PROP_VALUE_LABELS,
1284 g_param_spec_boolean ("value-labels",
1286 "Whether or not the data sheet should display labels instead of values",
1288 G_PARAM_WRITABLE | G_PARAM_READABLE));
1290 g_object_class_install_property (
1291 gobject_class, PROP_CASE_NUMBERS,
1292 g_param_spec_boolean ("case-numbers",
1294 "Whether or not the data sheet should display case numbers",
1296 G_PARAM_WRITABLE | G_PARAM_READABLE));
1298 g_object_class_install_property (
1301 g_param_spec_long ("current-case",
1303 "Zero based number of the selected case",
1306 G_PARAM_WRITABLE | G_PARAM_READABLE));
1308 g_object_class_install_property (
1310 PROP_MAY_CREATE_VARS,
1311 g_param_spec_boolean ("may-create-vars",
1312 "May create variables",
1313 "Whether the user may create more variables",
1315 G_PARAM_READWRITE));
1317 g_object_class_install_property (
1319 PROP_MAY_DELETE_VARS,
1320 g_param_spec_boolean ("may-delete-vars",
1321 "May delete variables",
1322 "Whether the user may delete variables",
1324 G_PARAM_READWRITE));
1326 g_object_class_install_property (
1329 g_param_spec_object ("ui-manager",
1331 "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.",
1332 GTK_TYPE_UI_MANAGER,
1337 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1339 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1342 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1343 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1347 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1349 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1353 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1354 gpointer user_data UNUSED)
1356 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1358 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1360 PsppSheetSelection *selection;
1362 selection = pspp_sheet_view_get_selection (sheet_view);
1363 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1367 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1368 &path, NULL, NULL, NULL))
1370 pspp_sheet_selection_unselect_all (selection);
1371 pspp_sheet_selection_select_path (selection, path);
1372 pspp_sheet_selection_select_all_columns (selection);
1373 gtk_tree_path_free (path);
1377 do_popup_menu (widget, event->button, event->time);
1386 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1388 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1389 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1390 const struct range_set_node *node;
1391 struct range_set *selected;
1393 selected = pspp_sheet_selection_get_range_set (selection);
1394 for (node = range_set_last (selected); node != NULL;
1395 node = range_set_prev (selected, node))
1397 unsigned long int start = range_set_node_get_start (node);
1398 unsigned long int count = range_set_node_get_width (node);
1400 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1402 range_set_destroy (selected);
1406 on_selection_changed (PsppSheetSelection *selection,
1407 gpointer user_data UNUSED)
1409 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1410 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1411 gint n_selected_rows;
1412 gboolean any_variables_selected;
1413 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1418 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1420 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1421 gtk_action_set_sensitive (action, n_selected_rows > 0);
1423 switch (n_selected_rows)
1426 may_delete_cases = FALSE;
1430 /* The row used for inserting new cases cannot be deleted. */
1431 path = gtk_tree_path_new_from_indices (
1432 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1433 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1435 gtk_tree_path_free (path);
1439 may_delete_cases = TRUE;
1442 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1443 gtk_action_set_sensitive (action, may_delete_cases);
1445 any_variables_selected = FALSE;
1446 may_delete_vars = may_insert_vars = FALSE;
1447 list = pspp_sheet_selection_get_selected_columns (selection);
1448 for (iter = list; iter != NULL; iter = iter->next)
1450 PsppSheetViewColumn *column = iter->data;
1451 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1455 may_delete_vars = may_insert_vars = TRUE;
1456 any_variables_selected = TRUE;
1459 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1460 may_insert_vars = TRUE;
1464 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1465 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1467 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1468 gtk_action_set_sensitive (action, may_insert_vars);
1470 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1471 gtk_action_set_sensitive (action, may_delete_vars);
1473 action = get_action_assert (data_sheet->builder, "sort-up");
1474 gtk_action_set_sensitive (action, may_delete_vars);
1476 action = get_action_assert (data_sheet->builder, "sort-down");
1477 gtk_action_set_sensitive (action, may_delete_vars);
1479 psppire_data_sheet_update_clip_actions (data_sheet);
1480 psppire_data_sheet_update_primary_selection (data_sheet,
1481 (n_selected_rows > 0
1482 && any_variables_selected));
1486 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1487 struct range_set **rowsp,
1488 struct range_set **colsp)
1490 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1491 PsppireDataStore *data_store = data_sheet->data_store;
1492 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1493 unsigned long n_cases;
1494 struct range_set *rows, *cols;
1497 if (data_store == NULL)
1499 n_cases = psppire_data_store_get_case_count (data_store);
1501 rows = pspp_sheet_selection_get_range_set (selection);
1502 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1503 if (range_set_is_empty (rows))
1505 range_set_destroy (rows);
1509 cols = range_set_create ();
1510 list = pspp_sheet_selection_get_selected_columns (selection);
1511 for (iter = list; iter != NULL; iter = iter->next)
1513 PsppSheetViewColumn *column = iter->data;
1514 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1517 range_set_set1 (cols, var_get_dict_index (var), 1);
1520 if (range_set_is_empty (cols))
1522 range_set_destroy (rows);
1523 range_set_destroy (cols);
1533 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1535 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1536 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1537 PsppireDataStore *data_store = data_sheet->data_store;
1538 struct range_set *selected;
1541 selected = pspp_sheet_selection_get_range_set (selection);
1542 row = range_set_scan (selected, 0);
1543 range_set_destroy (selected);
1545 if (row <= psppire_data_store_get_case_count (data_store))
1546 psppire_data_store_insert_new_case (data_store, row);
1550 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1552 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1553 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1554 PsppireDict *dict = data_sheet->data_store->dict;
1555 PsppSheetViewColumn *column;
1556 struct variable *var;
1561 list = pspp_sheet_selection_get_selected_columns (selection);
1564 column = list->data;
1567 var = g_object_get_data (G_OBJECT (column), "variable");
1568 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1569 if (psppire_dict_generate_name (dict, name, sizeof name))
1570 psppire_dict_insert_variable (dict, index, name);
1574 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1576 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1577 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1578 PsppireDict *dict = data_sheet->data_store->dict;
1581 list = pspp_sheet_selection_get_selected_columns (selection);
1584 list = g_list_reverse (list);
1585 for (iter = list; iter; iter = iter->next)
1587 PsppSheetViewColumn *column = iter->data;
1588 struct variable *var;
1590 var = g_object_get_data (G_OBJECT (column), "variable");
1592 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1604 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1606 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1607 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1608 PsppireDataWindow *pdw;
1613 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1614 g_return_if_fail (pdw != NULL);
1616 list = pspp_sheet_selection_get_selected_columns (selection);
1618 syntax = g_string_new ("SORT CASES BY");
1620 for (iter = list; iter; iter = iter->next)
1622 PsppSheetViewColumn *column = iter->data;
1623 struct variable *var;
1625 var = g_object_get_data (G_OBJECT (column), "variable");
1628 g_string_append_printf (syntax, " %s", var_get_name (var));
1634 if (order == SORT_DESCEND)
1635 g_string_append (syntax, " (DOWN)");
1636 g_string_append_c (syntax, '.');
1637 execute_const_syntax_string (pdw, syntax->str);
1639 g_string_free (syntax, TRUE);
1643 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1645 do_sort (data_sheet, SORT_ASCEND);
1649 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1651 do_sort (data_sheet, SORT_DESCEND);
1655 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1657 goto_case_dialog (data_sheet);
1661 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1663 PsppireDataWindow *pdw;
1665 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1666 g_return_if_fail (pdw != NULL);
1672 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1674 psppire_data_sheet_set_clip (data_sheet, FALSE);
1678 on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet)
1680 psppire_data_sheet_set_clip (data_sheet, TRUE);
1684 on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet)
1686 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
1687 GtkClipboard *clipboard =
1688 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1690 gtk_clipboard_request_contents (clipboard,
1691 gdk_atom_intern ("UTF8_STRING", TRUE),
1692 psppire_data_sheet_clip_received_cb,
1697 psppire_data_sheet_init (PsppireDataSheet *obj)
1699 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1702 obj->show_value_labels = FALSE;
1703 obj->show_case_numbers = TRUE;
1704 obj->may_create_vars = TRUE;
1705 obj->may_delete_vars = TRUE;
1707 obj->owns_primary_selection = FALSE;
1709 obj->scroll_to_bottom_signal = 0;
1710 obj->scroll_to_right_signal = 0;
1711 obj->on_owner_change_signal = 0;
1712 obj->new_variable_column = NULL;
1713 obj->container = NULL;
1716 obj->dispose_has_run = FALSE;
1718 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1720 g_signal_connect (obj, "notify::model",
1721 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1723 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1724 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1725 PSPP_SHEET_SELECTION_RECTANGLE);
1727 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1728 g_signal_connect (obj, "query-tooltip",
1729 G_CALLBACK (on_query_tooltip), NULL);
1730 g_signal_connect (obj, "button-press-event",
1731 G_CALLBACK (on_button_pressed), NULL);
1732 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1734 obj->builder = builder_new ("data-sheet.ui");
1736 action = get_action_assert (obj->builder, "edit_clear-cases");
1737 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1739 gtk_action_set_sensitive (action, FALSE);
1740 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1741 "changed", G_CALLBACK (on_selection_changed), NULL);
1743 action = get_action_assert (obj->builder, "edit_insert-case");
1744 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1747 action = get_action_assert (obj->builder, "edit_insert-variable");
1748 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1751 action = get_action_assert (obj->builder, "edit_goto-case");
1752 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1755 action = get_action_assert (obj->builder, "edit_copy");
1756 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1758 action = get_action_assert (obj->builder, "edit_cut");
1759 g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj);
1761 action = get_action_assert (obj->builder, "edit_paste");
1762 g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj);
1764 action = get_action_assert (obj->builder, "edit_clear-variables");
1765 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1768 action = get_action_assert (obj->builder, "edit_find");
1769 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1771 action = get_action_assert (obj->builder, "sort-up");
1772 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1774 action = get_action_assert (obj->builder, "sort-down");
1775 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1780 psppire_data_sheet_new (void)
1782 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1786 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1788 return data_sheet->data_store;
1792 refresh_model (PsppireDataSheet *data_sheet)
1794 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1796 if (data_sheet->data_store != NULL)
1798 PsppireEmptyListStore *model;
1802 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1803 model = psppire_empty_list_store_new (n_rows);
1804 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1805 GTK_TREE_MODEL (model));
1806 g_object_unref (model);
1808 action = get_action_assert (data_sheet->builder, "edit_copy");
1809 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1815 on_case_inserted (PsppireDataStore *data_store, gint row,
1816 PsppireDataSheet *data_sheet)
1818 PsppireEmptyListStore *empty_list_store;
1819 GtkTreeModel *tree_model;
1822 g_return_if_fail (data_store == data_sheet->data_store);
1824 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1825 if (row == n_rows - 1)
1828 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1829 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1830 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1831 psppire_empty_list_store_row_inserted (empty_list_store, row);
1835 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1836 PsppireDataSheet *data_sheet)
1839 g_return_if_fail (data_store == data_sheet->data_store);
1843 /* This is a bit of a cop-out. We could do better, if it ever turns out
1844 that this performs too poorly. */
1845 refresh_model (data_sheet);
1849 PsppireEmptyListStore *empty_list_store;
1850 GtkTreeModel *tree_model;
1851 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1853 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1854 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1855 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1856 psppire_empty_list_store_row_deleted (empty_list_store, first);
1861 on_case_change (PsppireDataStore *data_store, gint row,
1862 PsppireDataSheet *data_sheet)
1864 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1866 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1867 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1871 on_backend_changed (PsppireDataStore *data_store,
1872 PsppireDataSheet *data_sheet)
1874 g_return_if_fail (data_store == data_sheet->data_store);
1875 refresh_model (data_sheet);
1879 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1880 PsppireDataSheet *data_sheet)
1882 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1883 PsppSheetViewColumn *column;
1884 struct variable *var;
1888 g_return_if_fail (data_sheet->data_store != NULL);
1889 g_return_if_fail (dict == data_sheet->data_store->dict);
1891 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1896 var = psppire_dict_get_variable (data_store->dict, dict_index);
1897 g_return_if_fail (var != NULL);
1899 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1900 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1901 if (display_width != var_get_display_width (var))
1903 gint base_width, incr_width;
1905 display_width = var_get_display_width (var);
1906 calc_width_conversion (data_sheet, &base_width, &incr_width);
1907 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1908 base_width, incr_width);
1909 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1914 on_variable_changed (PsppireDict *dict, int dict_index,
1915 guint what, const struct variable *oldvar,
1916 PsppireDataSheet *data_sheet)
1918 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1919 PsppSheetViewColumn *column;
1920 GtkCellRenderer *cell;
1921 struct variable *var;
1925 g_return_if_fail (data_sheet->data_store != NULL);
1926 g_return_if_fail (dict == data_sheet->data_store->dict);
1929 if (what & VAR_TRAIT_DISPLAY_WIDTH)
1930 on_variable_display_width_changed (dict, dict_index, data_sheet);
1932 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1938 var = psppire_dict_get_variable (data_store->dict, dict_index);
1939 g_return_if_fail (var != NULL);
1941 name = escape_underscores (var_get_name (var));
1942 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1943 pspp_sheet_view_column_set_title (column, name);
1946 cells = pspp_sheet_view_column_get_cell_renderers (column);
1947 g_return_if_fail (cells);
1949 g_list_free (cells);
1951 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1953 /* Stop editing before we delete and replace the cell renderers.
1954 Otherwise if this column is currently being edited, an eventual call
1955 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1956 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1958 It's possible that this is a bug in PsppSheetView, and it's possible
1959 that PsppSheetView inherits that from GtkTreeView, but I haven't
1960 investigated yet. */
1961 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1963 add_data_column_cell_renderer (data_sheet, column);
1968 on_variable_inserted (PsppireDict *dict, int var_index,
1969 PsppireDataSheet *data_sheet)
1971 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1972 gint base_width, incr_width;
1973 PsppSheetViewColumn *column;
1975 calc_width_conversion (data_sheet, &base_width, &incr_width);
1976 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1977 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1981 on_variable_deleted (PsppireDict *dict,
1982 const struct variable *var, int case_idx, int width,
1983 PsppireDataSheet *data_sheet)
1985 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1986 GList *columns, *iter;
1988 columns = pspp_sheet_view_get_columns (sheet_view);
1989 for (iter = columns; iter != NULL; iter = iter->next)
1991 PsppSheetViewColumn *column = iter->data;
1992 const struct variable *column_var;
1994 column_var = g_object_get_data (G_OBJECT (column), "variable");
1995 if (column_var == var)
1996 pspp_sheet_view_remove_column (sheet_view, column);
1998 g_list_free (columns);
2002 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
2004 PsppireDataStore *store = data_sheet->data_store;
2009 data_sheet->data_store = NULL;
2011 g_signal_handlers_disconnect_by_func (
2012 store, G_CALLBACK (on_backend_changed), data_sheet);
2013 g_signal_handlers_disconnect_by_func (
2014 store, G_CALLBACK (on_case_inserted), data_sheet);
2015 g_signal_handlers_disconnect_by_func (
2016 store, G_CALLBACK (on_cases_deleted), data_sheet);
2017 g_signal_handlers_disconnect_by_func (
2018 store, G_CALLBACK (on_case_change), data_sheet);
2020 g_signal_handlers_disconnect_by_func (
2021 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
2022 g_signal_handlers_disconnect_by_func (
2023 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
2024 g_signal_handlers_disconnect_by_func (
2025 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
2026 g_signal_handlers_disconnect_by_func (
2027 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
2029 g_object_unref (store);
2033 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
2034 PsppireDataStore *data_store)
2036 psppire_data_sheet_unset_data_store (data_sheet);
2038 data_sheet->data_store = data_store;
2039 if (data_store != NULL)
2041 g_object_ref (data_store);
2042 g_signal_connect (data_store, "backend-changed",
2043 G_CALLBACK (on_backend_changed), data_sheet);
2044 g_signal_connect (data_store, "case-inserted",
2045 G_CALLBACK (on_case_inserted), data_sheet);
2046 g_signal_connect (data_store, "cases-deleted",
2047 G_CALLBACK (on_cases_deleted), data_sheet);
2048 g_signal_connect (data_store, "case-changed",
2049 G_CALLBACK (on_case_change), data_sheet);
2051 /* XXX it's unclean to hook into the dict this way--what if the dict
2052 changes? As of this writing, though, nothing ever changes the
2053 data_store's dict. */
2054 g_signal_connect (data_store->dict, "variable-changed",
2055 G_CALLBACK (on_variable_changed),
2057 g_signal_connect (data_store->dict, "variable-inserted",
2058 G_CALLBACK (on_variable_inserted), data_sheet);
2059 g_signal_connect (data_store->dict, "variable-deleted",
2060 G_CALLBACK (on_variable_deleted), data_sheet);
2062 refresh_model (data_sheet);
2065 /* Clipboard stuff */
2067 /* A casereader and dictionary holding the data currently in the clip */
2068 static struct casereader *clip_datasheet = NULL;
2069 static struct dictionary *clip_dict = NULL;
2072 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2075 psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
2076 struct casereader **readerp,
2077 struct dictionary **dictp)
2079 struct casewriter *writer ;
2080 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2081 struct case_map *map = NULL;
2082 struct range_set *rows, *cols;
2083 const struct range_set_node *node;
2084 struct dictionary *dict;
2086 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2093 /* Construct clip dictionary. */
2094 *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
2095 RANGE_SET_FOR_EACH (node, cols)
2099 for (dict_index = node->start; dict_index < node->end; dict_index++)
2101 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2102 dict_clone_var_assert (dict, var);
2106 /* Construct clip data. */
2107 map = case_map_by_name (ds->dict->dict, dict);
2108 writer = autopaging_writer_create (dict_get_proto (dict));
2109 RANGE_SET_FOR_EACH (node, rows)
2111 unsigned long int row;
2113 for (row = node->start; row < node->end; row++)
2115 struct ccase *old = psppire_data_store_get_case (ds, row);
2117 casewriter_write (writer, case_map_execute (map, old));
2119 casewriter_force_error (writer);
2122 case_map_destroy (map);
2124 /* Clear data that we copied out, if we're doing a "cut". */
2125 if (cut && !casewriter_error (writer))
2127 RANGE_SET_FOR_EACH (node, rows)
2129 unsigned long int row;
2131 for (row = node->start; row < node->end; row++)
2133 const struct range_set_node *node2;
2135 RANGE_SET_FOR_EACH (node2, cols)
2139 for (dict_index = node2->start; dict_index < node2->end;
2142 struct variable *var;
2144 var = dict_get_var (ds->dict->dict, dict_index);
2145 psppire_data_store_set_string (ds, "", row,
2153 range_set_destroy (rows);
2154 range_set_destroy (cols);
2156 *readerp = casewriter_make_reader (writer);
2161 /* Set the clip from the currently selected range in DATA_SHEET. If CUT is
2162 true, clears the original data from DATA_SHEET, otherwise leaves the
2163 original data in-place. */
2165 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2168 struct casereader *reader;
2169 struct dictionary *dict;
2171 if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
2173 casereader_destroy (clip_datasheet);
2174 dict_destroy (clip_dict);
2176 clip_datasheet = reader;
2179 psppire_data_sheet_update_clipboard (data_sheet);
2190 /* Perform data_out for case CC, variable V, appending to STRING */
2192 data_out_g_string (GString *string, const struct variable *v,
2193 const struct ccase *cc)
2195 const struct fmt_spec *fs = var_get_print_format (v);
2196 const union value *val = case_data (cc, v);
2198 char *s = data_out (val, var_get_encoding (v), fs);
2200 g_string_append (string, s);
2206 clip_to_text (struct casereader *datasheet, struct dictionary *dict)
2211 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2212 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2213 const size_t var_cnt = dict_get_var_cnt (dict);
2215 string = g_string_sized_new (10 * val_cnt * case_cnt);
2217 for (r = 0 ; r < case_cnt ; ++r )
2222 cc = casereader_peek (datasheet, r);
2225 g_warning ("Clipboard seems to have inexplicably shrunk");
2229 for (c = 0 ; c < var_cnt ; ++c)
2231 const struct variable *v = dict_get_var (dict, c);
2232 data_out_g_string (string, v, cc);
2233 if ( c < val_cnt - 1 )
2234 g_string_append (string, "\t");
2238 g_string_append (string, "\n");
2248 clip_to_html (struct casereader *datasheet, struct dictionary *dict)
2253 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2254 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2255 const size_t var_cnt = dict_get_var_cnt (dict);
2257 /* Guestimate the size needed */
2258 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2260 g_string_append (string,
2261 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2263 g_string_append (string, "<table>\n");
2264 for (r = 0 ; r < case_cnt ; ++r )
2267 struct ccase *cc = casereader_peek (datasheet, r);
2270 g_warning ("Clipboard seems to have inexplicably shrunk");
2273 g_string_append (string, "<tr>\n");
2275 for (c = 0 ; c < var_cnt ; ++c)
2277 const struct variable *v = dict_get_var (dict, c);
2278 g_string_append (string, "<td>");
2279 data_out_g_string (string, v, cc);
2280 g_string_append (string, "</td>\n");
2283 g_string_append (string, "</tr>\n");
2287 g_string_append (string, "</table>\n");
2295 psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
2297 struct casereader *reader,
2298 struct dictionary *dict)
2300 GString *string = NULL;
2304 case SELECT_FMT_TEXT:
2305 string = clip_to_text (reader, dict);
2307 case SELECT_FMT_HTML:
2308 string = clip_to_html (reader, dict);
2311 g_assert_not_reached ();
2314 gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
2316 (const guchar *) string->str, string->len);
2318 g_string_free (string, TRUE);
2322 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2323 GtkSelectionData *selection_data,
2327 psppire_data_sheet_clipboard_set (selection_data, info,
2328 clip_datasheet, clip_dict);
2332 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2335 dict_destroy (clip_dict);
2338 casereader_destroy (clip_datasheet);
2339 clip_datasheet = NULL;
2343 static const GtkTargetEntry targets[] = {
2344 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2345 { "STRING", 0, SELECT_FMT_TEXT },
2346 { "TEXT", 0, SELECT_FMT_TEXT },
2347 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2348 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2349 { "text/plain", 0, SELECT_FMT_TEXT },
2350 { "text/html", 0, SELECT_FMT_HTML }
2356 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2358 GtkClipboard *clipboard =
2359 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2360 GDK_SELECTION_CLIPBOARD);
2362 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2363 G_N_ELEMENTS (targets),
2364 psppire_data_sheet_clipboard_get_cb,
2365 psppire_data_sheet_clipboard_clear_cb,
2367 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2371 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2373 struct range_set *rows, *cols;
2377 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2380 range_set_destroy (rows);
2381 range_set_destroy (cols);
2384 action = get_action_assert (data_sheet->builder, "edit_copy");
2385 gtk_action_set_sensitive (action, enable);
2387 action = get_action_assert (data_sheet->builder, "edit_cut");
2388 gtk_action_set_sensitive (action, enable);
2392 psppire_data_sheet_primary_get_cb (GtkClipboard *clipboard,
2393 GtkSelectionData *selection_data,
2397 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2398 struct casereader *reader;
2399 struct dictionary *dict;
2401 if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
2403 psppire_data_sheet_clipboard_set (selection_data, info,
2405 casereader_destroy (reader);
2406 dict_destroy (dict);
2411 psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
2412 gboolean should_own)
2414 GtkClipboard *clipboard;
2415 GdkDisplay *display;
2417 display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
2418 clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
2419 g_return_if_fail (clipboard != NULL);
2421 if (data_sheet->owns_primary_selection && !should_own)
2423 data_sheet->owns_primary_selection = FALSE;
2424 gtk_clipboard_clear (clipboard);
2426 else if (should_own)
2427 data_sheet->owns_primary_selection =
2428 gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2429 psppire_data_sheet_primary_get_cb,
2430 NULL, G_OBJECT (data_sheet));
2433 /* A callback for when the clipboard contents have been received. */
2435 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2436 GtkSelectionData *sd,
2439 PsppireDataSheet *data_sheet = data;
2440 PsppireDataStore *store = data_sheet->data_store;
2441 struct range_set *rows, *cols;
2443 gint next_row, next_column;
2447 if ( gtk_selection_data_get_length (sd) < 0 )
2450 if ( gtk_selection_data_get_data_type (sd) != gdk_atom_intern ("UTF8_STRING", FALSE))
2453 c = (char *) gtk_selection_data_get_data (sd);
2455 /* Get the starting selected position in the data sheet. (Possibly we should
2456 only paste into the selected range if it's larger than one cell?) */
2457 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2459 next_row = range_set_first (rows)->start;
2460 first_column = next_column = range_set_first (cols)->start;
2461 range_set_destroy (rows);
2462 range_set_destroy (cols);
2464 g_return_if_fail (next_row >= 0);
2465 g_return_if_fail (next_column >= 0);
2467 while (count < gtk_selection_data_get_length (sd))
2469 gint row = next_row;
2470 gint column = next_column;
2471 struct variable *var;
2474 while (*c != '\t' && *c != '\n' && count < gtk_selection_data_get_length (sd))
2482 next_column = column + 1;
2484 else if ( *c == '\n')
2487 next_column = first_column;
2492 var = psppire_dict_get_variable (store->dict, column);
2494 psppire_data_store_set_string (store, s, row, var, FALSE);
2499 psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
2504 GtkAction *action = GTK_ACTION (data);
2505 gboolean compatible_target;
2508 compatible_target = FALSE;
2509 for (i = 0; i < G_N_ELEMENTS (targets); i++)
2511 GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
2514 for (j = 0; j < n_atoms; j++)
2515 if (target == atoms[j])
2517 compatible_target = TRUE;
2522 gtk_action_set_sensitive (action, compatible_target);
2523 g_object_unref (action);
2527 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2529 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2530 GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste");
2532 g_object_ref (action);
2533 gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb,