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);
601 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
603 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
604 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
605 PsppireDataWindow *pdw;
610 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
611 g_return_if_fail (pdw != NULL);
613 list = pspp_sheet_selection_get_selected_columns (selection);
615 syntax = g_string_new ("SORT CASES BY");
617 for (iter = list; iter; iter = iter->next)
619 PsppSheetViewColumn *column = iter->data;
620 struct variable *var;
622 var = g_object_get_data (G_OBJECT (column), "variable");
625 g_string_append_printf (syntax, " %s", var_get_name (var));
631 if (order == SORT_DESCEND)
632 g_string_append (syntax, " (DOWN)");
633 g_string_append_c (syntax, '.');
634 execute_const_syntax_string (pdw, syntax->str);
636 g_string_free (syntax, TRUE);
640 on_sort_up (PsppireDataSheet *data_sheet)
642 do_sort (data_sheet, SORT_ASCEND);
646 on_sort_down (PsppireDataSheet *data_sheet)
648 do_sort (data_sheet, SORT_DESCEND);
652 do_data_column_popup_menu (PsppSheetViewColumn *column,
653 guint button, guint32 time)
655 GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
656 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
658 gtk_menu_popup (GTK_MENU (data_sheet->column_popup_menu), NULL, NULL, NULL, NULL, button, time);
662 on_data_column_popup_menu (PsppSheetViewColumn *column,
663 gpointer user_data UNUSED)
665 do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
669 on_column_button_press_event (PsppSheetViewColumn *column,
670 GdkEventButton *event,
671 gpointer user_data UNUSED)
673 PsppSheetSelection *selection;
674 PsppSheetView *sheet_view;
676 sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
678 g_return_val_if_fail (sheet_view != NULL, FALSE);
680 selection = pspp_sheet_view_get_selection (sheet_view);
681 g_return_val_if_fail (selection != NULL, FALSE);
683 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
685 do_data_column_popup_menu (column, event->button, event->time);
688 else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
690 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
691 struct variable *var;
693 var = g_object_get_data (G_OBJECT (column), "variable");
698 g_signal_emit_by_name (data_sheet, "var-double-clicked",
699 var_get_dict_index (var), &handled);
708 on_data_column_query_tooltip (PsppSheetViewColumn *column,
710 gpointer user_data UNUSED)
712 struct variable *var;
715 var = g_object_get_data (G_OBJECT (column), "variable");
716 g_return_val_if_fail (var != NULL, FALSE);
718 text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
719 gtk_tooltip_set_text (tooltip, text);
725 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
726 PsppSheetViewColumn *column)
728 GtkCellRenderer *cell;
729 struct variable *var;
731 var = g_object_get_data (G_OBJECT (column), "variable");
732 g_return_if_fail (var != NULL);
734 if (data_sheet->show_value_labels && var_has_value_labels (var))
736 cell = gtk_cell_renderer_combo_new ();
737 g_object_set (G_OBJECT (cell),
743 cell = gtk_cell_renderer_text_new ();
745 g_signal_connect (cell, "editing-started",
746 G_CALLBACK (on_data_column_editing_started), NULL);
747 g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
749 g_object_set_data (G_OBJECT (cell), "column", column);
750 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
752 pspp_sheet_view_column_clear (column);
753 pspp_sheet_view_column_pack_start (column, cell, TRUE);
755 pspp_sheet_view_column_set_cell_data_func (
756 column, cell, render_data_cell, data_sheet, NULL);
759 static PsppSheetViewColumn *
760 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
761 gint base_width, gint incr_width)
763 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
764 struct variable *var;
765 PsppSheetViewColumn *column;
769 var = psppire_dict_get_variable (data_store->dict, dict_idx);
771 column = pspp_sheet_view_column_new ();
773 name = escape_underscores (var_get_name (var));
774 pspp_sheet_view_column_set_title (column, name);
777 g_object_set_data (G_OBJECT (column), "variable", var);
779 width = display_width_to_pixel_width (data_sheet,
780 var_get_display_width (var),
781 base_width, incr_width);
782 pspp_sheet_view_column_set_min_width (column, 10);
783 pspp_sheet_view_column_set_fixed_width (column, width);
784 pspp_sheet_view_column_set_resizable (column, TRUE);
786 pspp_sheet_view_column_set_clickable (column, TRUE);
787 g_signal_connect (column, "notify::width",
788 G_CALLBACK (on_data_column_resized), data_sheet);
790 g_signal_connect (column, "button-press-event",
791 G_CALLBACK (on_column_button_press_event),
793 g_signal_connect (column, "query-tooltip",
794 G_CALLBACK (on_data_column_query_tooltip), NULL);
795 g_signal_connect (column, "popup-menu",
796 G_CALLBACK (on_data_column_popup_menu), data_sheet);
798 add_data_column_cell_renderer (data_sheet, column);
804 make_new_variable_column (PsppireDataSheet *data_sheet,
805 gint base_width, gint incr_width)
807 PsppSheetViewColumn *column;
808 GtkCellRenderer *cell;
811 cell = gtk_cell_renderer_text_new ();
812 g_object_set (cell, "editable", TRUE, NULL);
814 g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
817 column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
818 g_object_set_data (G_OBJECT (column), "new-var-column", column);
820 width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
821 pspp_sheet_view_column_set_min_width (column, 10);
822 pspp_sheet_view_column_set_fixed_width (column, width);
823 pspp_sheet_view_column_set_tabbable (column, FALSE);
825 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
826 g_signal_connect (column, "button-press-event",
827 G_CALLBACK (on_column_button_press_event),
829 g_signal_connect (column, "popup-menu",
830 G_CALLBACK (on_data_column_popup_menu), data_sheet);
832 pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
834 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
835 data_sheet->new_variable_column = column;
839 psppire_data_sheet_model_changed (GObject *gobject,
843 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
844 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
845 PsppireDataStore *data_store;
847 /* Remove old columns. */
850 PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
854 pspp_sheet_view_remove_column (sheet_view, column);
856 data_sheet->new_variable_column = NULL;
858 if (pspp_sheet_view_get_model (sheet_view) == NULL)
860 /* Don't create any columns at all if there's no model. Otherwise we'll
861 create some columns as part of the "dispose" callback for the sheet
862 view, which sets the model to NULL. That causes warnings to be
863 logged and is obviously undesirable in any case. */
867 /* Add new columns. */
868 data_store = psppire_data_sheet_get_data_store (data_sheet);
869 if (data_store != NULL)
871 gint base_width, incr_width;
874 calc_width_conversion (data_sheet, &base_width, &incr_width);
876 make_row_number_column (data_sheet, data_store);
877 for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
879 PsppSheetViewColumn *column;
881 column = make_data_column (data_sheet, i, base_width, incr_width);
882 pspp_sheet_view_append_column (sheet_view, column);
884 make_new_variable_column (data_sheet, base_width, incr_width);
895 PROP_MAY_CREATE_VARS,
900 psppire_data_sheet_set_property (GObject *object,
905 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
909 case PROP_DATA_STORE:
910 psppire_data_sheet_set_data_store (
911 obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
914 case PROP_VALUE_LABELS:
915 psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
918 case PROP_CASE_NUMBERS:
919 psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
922 case PROP_CURRENT_CASE:
923 psppire_data_sheet_goto_case (obj, g_value_get_long (value));
926 case PROP_MAY_CREATE_VARS:
927 psppire_data_sheet_set_may_create_vars (obj,
928 g_value_get_boolean (value));
931 case PROP_MAY_DELETE_VARS:
932 psppire_data_sheet_set_may_delete_vars (obj,
933 g_value_get_boolean (value));
937 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
943 psppire_data_sheet_get_property (GObject *object,
948 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
952 case PROP_DATA_STORE:
953 g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
956 case PROP_VALUE_LABELS:
957 g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
960 case PROP_CASE_NUMBERS:
961 g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
964 case PROP_CURRENT_CASE:
965 g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
968 case PROP_MAY_CREATE_VARS:
969 g_value_set_boolean (value, obj->may_create_vars);
972 case PROP_MAY_DELETE_VARS:
973 g_value_set_boolean (value, obj->may_delete_vars);
977 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
983 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
985 return ds->show_value_labels;
989 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
990 gboolean show_value_labels)
992 show_value_labels = !!show_value_labels;
993 if (show_value_labels != ds->show_value_labels)
995 ds->show_value_labels = show_value_labels;
996 g_object_notify (G_OBJECT (ds), "value-labels");
998 /* Pretend the model changed, to force the columns to be rebuilt.
999 Otherwise cell renderers won't get changed from combo boxes to text
1000 entries or vice versa. */
1001 g_object_notify (G_OBJECT (ds), "model");
1006 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
1008 return ds->show_case_numbers;
1012 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
1013 gboolean show_case_numbers)
1015 show_case_numbers = !!show_case_numbers;
1016 if (show_case_numbers != ds->show_case_numbers)
1018 PsppSheetViewColumn *column;
1020 ds->show_case_numbers = show_case_numbers;
1021 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
1023 pspp_sheet_view_column_set_visible (column, show_case_numbers);
1025 g_object_notify (G_OBJECT (ds), "case-numbers");
1026 gtk_widget_queue_draw (GTK_WIDGET (ds));
1031 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
1033 return data_sheet->may_create_vars;
1037 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
1038 gboolean may_create_vars)
1040 if (data_sheet->may_create_vars != may_create_vars)
1042 data_sheet->may_create_vars = may_create_vars;
1043 if (data_sheet->new_variable_column)
1044 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
1047 on_selection_changed (pspp_sheet_view_get_selection (
1048 PSPP_SHEET_VIEW (data_sheet)), NULL);
1053 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
1055 return data_sheet->may_delete_vars;
1059 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
1060 gboolean may_delete_vars)
1062 if (data_sheet->may_delete_vars != may_delete_vars)
1064 data_sheet->may_delete_vars = may_delete_vars;
1065 on_selection_changed (pspp_sheet_view_get_selection (
1066 PSPP_SHEET_VIEW (data_sheet)), NULL);
1070 static PsppSheetViewColumn *
1071 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1074 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1075 PsppireDataStore *data_store;
1076 PsppSheetViewColumn *column;
1077 struct variable *var;
1080 data_store = psppire_data_sheet_get_data_store (data_sheet);
1081 g_return_val_if_fail (data_store != NULL, NULL);
1082 g_return_val_if_fail (data_store->dict != NULL, NULL);
1084 var = psppire_dict_get_variable (data_store->dict, dict_index);
1085 g_return_val_if_fail (var != NULL, NULL);
1088 list = pspp_sheet_view_get_columns (sheet_view);
1089 for (iter = list; iter != NULL; iter = iter->next)
1091 PsppSheetViewColumn *c = iter->data;
1094 v = g_object_get_data (G_OBJECT (c), "variable");
1107 psppire_data_sheet_goto_variable (PsppireDataSheet *data_sheet,
1110 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1111 PsppSheetViewColumn *column;
1113 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1119 gint row = psppire_data_sheet_get_current_case (data_sheet);
1120 path = gtk_tree_path_new_from_indices (row >= 0 ? row : 0, -1);
1122 pspp_sheet_view_scroll_to_cell (sheet_view, path, column,
1124 pspp_sheet_view_set_cursor (sheet_view, path, column, FALSE);
1125 gtk_tree_path_free (path);
1130 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1132 PsppSheetSelection *selection;
1133 struct variable *var;
1134 GList *selected_columns;
1137 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1138 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1141 for (iter = selected_columns; iter != NULL; iter = iter->next)
1143 PsppSheetViewColumn *column = iter->data;
1144 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1157 g_list_free (selected_columns);
1163 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1165 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1166 PsppireDataStore *store = data_sheet->data_store;
1167 PsppSheetSelection *selection;
1170 g_return_if_fail (case_index >= 0);
1171 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1173 path = gtk_tree_path_new_from_indices (case_index, -1);
1175 /* Select the case. */
1176 selection = pspp_sheet_view_get_selection (sheet_view);
1177 pspp_sheet_selection_unselect_all (selection);
1178 pspp_sheet_selection_select_path (selection, path);
1179 pspp_sheet_selection_select_all_columns (selection);
1181 /* Scroll so that the case is visible. */
1182 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1184 gtk_tree_path_free (path);
1187 /* Returns the 0-based index of a selected case, if there is at least one, and
1190 If more than one case is selected, returns the one with the smallest index,
1191 that is, the index of the case closest to the beginning of the file. The
1192 row that can be used to insert a new case is not considered a case. */
1194 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1196 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1197 PsppireDataStore *store = data_sheet->data_store;
1198 const struct range_set_node *node;
1199 PsppSheetSelection *selection;
1200 struct range_set *rows;
1203 selection = pspp_sheet_view_get_selection (sheet_view);
1204 rows = pspp_sheet_selection_get_range_set (selection);
1205 node = range_set_first (rows);
1206 row = (node && node->start < psppire_data_store_get_case_count (store)
1209 range_set_destroy (rows);
1214 /* Returns the 0-based index of a selected case, if exactly one case is
1215 selected, and -1 otherwise. Returns -1 if the row that can be used to
1216 insert a new case is selected. */
1218 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1220 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1221 PsppireDataStore *store = data_sheet->data_store;
1222 const struct range_set_node *node;
1223 PsppSheetSelection *selection;
1224 struct range_set *rows;
1227 selection = pspp_sheet_view_get_selection (sheet_view);
1228 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1231 rows = pspp_sheet_selection_get_range_set (selection);
1232 node = range_set_first (rows);
1233 row = (node && node->start < psppire_data_store_get_case_count (store)
1236 range_set_destroy (rows);
1243 psppire_data_sheet_dispose (GObject *object)
1245 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1247 if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0)
1249 g_signal_handler_disconnect (data_sheet->clip,
1250 data_sheet->on_owner_change_signal);
1251 data_sheet->on_owner_change_signal = 0;
1254 if (data_sheet->dispose_has_run)
1257 data_sheet->dispose_has_run = TRUE;
1259 psppire_data_sheet_unset_data_store (data_sheet);
1261 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1265 psppire_data_sheet_map (GtkWidget *widget)
1267 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1269 GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
1271 data_sheet->clip = gtk_widget_get_clipboard (widget,
1272 GDK_SELECTION_CLIPBOARD);
1273 if (data_sheet->on_owner_change_signal)
1274 g_signal_handler_disconnect (data_sheet->clip,
1275 data_sheet->on_owner_change_signal);
1276 data_sheet->on_owner_change_signal
1277 = g_signal_connect (data_sheet->clip, "owner-change",
1278 G_CALLBACK (on_owner_change), widget);
1279 on_owner_change (data_sheet->clip, NULL, widget);
1283 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1285 GObjectClass *gobject_class;
1286 GtkWidgetClass *widget_class;
1288 gobject_class = G_OBJECT_CLASS (class);
1289 gobject_class->set_property = psppire_data_sheet_set_property;
1290 gobject_class->get_property = psppire_data_sheet_get_property;
1291 gobject_class->dispose = psppire_data_sheet_dispose;
1293 widget_class = GTK_WIDGET_CLASS (class);
1294 widget_class->map = psppire_data_sheet_map;
1296 g_signal_new ("var-double-clicked",
1297 G_OBJECT_CLASS_TYPE (gobject_class),
1300 g_signal_accumulator_true_handled, NULL,
1301 psppire_marshal_BOOLEAN__INT,
1302 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1304 g_object_class_install_property (
1305 gobject_class, PROP_DATA_STORE,
1306 g_param_spec_object ("data-store",
1308 "The data store for the data sheet to display.",
1309 PSPPIRE_TYPE_DATA_STORE,
1310 G_PARAM_WRITABLE | G_PARAM_READABLE));
1312 g_object_class_install_property (
1313 gobject_class, PROP_VALUE_LABELS,
1314 g_param_spec_boolean ("value-labels",
1316 "Whether or not the data sheet should display labels instead of values",
1318 G_PARAM_WRITABLE | G_PARAM_READABLE));
1320 g_object_class_install_property (
1321 gobject_class, PROP_CASE_NUMBERS,
1322 g_param_spec_boolean ("case-numbers",
1324 "Whether or not the data sheet should display case numbers",
1326 G_PARAM_WRITABLE | G_PARAM_READABLE));
1328 g_object_class_install_property (
1331 g_param_spec_long ("current-case",
1333 "Zero based number of the selected case",
1336 G_PARAM_WRITABLE | G_PARAM_READABLE));
1338 g_object_class_install_property (
1340 PROP_MAY_CREATE_VARS,
1341 g_param_spec_boolean ("may-create-vars",
1342 "May create variables",
1343 "Whether the user may create more variables",
1345 G_PARAM_READWRITE));
1348 g_object_class_install_property (
1350 PROP_MAY_DELETE_VARS,
1351 g_param_spec_boolean ("may-delete-vars",
1352 "May delete variables",
1353 "Whether the user may delete variables",
1355 G_PARAM_READWRITE));
1359 do_row_popup_menu (GtkWidget *widget, guint button, guint32 time)
1361 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1364 gtk_menu_popup (GTK_MENU (data_sheet->row_popup_menu), NULL, NULL, NULL, NULL, button, time);
1368 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1370 do_row_popup_menu (widget, 0, gtk_get_current_event_time ());
1374 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1375 gpointer user_data UNUSED)
1377 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1379 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1381 PsppSheetSelection *selection;
1383 selection = pspp_sheet_view_get_selection (sheet_view);
1384 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1388 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1389 &path, NULL, NULL, NULL))
1391 pspp_sheet_selection_unselect_all (selection);
1392 pspp_sheet_selection_select_path (selection, path);
1393 pspp_sheet_selection_select_all_columns (selection);
1394 gtk_tree_path_free (path);
1398 do_row_popup_menu (widget, event->button, event->time);
1407 psppire_data_sheet_edit_clear_cases (PsppireDataSheet *data_sheet)
1409 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1410 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1411 const struct range_set_node *node;
1412 struct range_set *selected;
1414 selected = pspp_sheet_selection_get_range_set (selection);
1415 for (node = range_set_last (selected); node != NULL;
1416 node = range_set_prev (selected, node))
1418 unsigned long int start = range_set_node_get_start (node);
1419 unsigned long int count = range_set_node_get_width (node);
1421 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1423 range_set_destroy (selected);
1427 on_selection_changed (PsppSheetSelection *selection,
1428 gpointer user_data UNUSED)
1430 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1431 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1432 gboolean any_variables_selected;
1433 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1437 GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (data_sheet));
1438 if (! PSPPIRE_IS_DATA_WINDOW (top))
1441 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top);
1443 gint n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1445 gtk_widget_set_sensitive (dw->mi_insert_case, n_selected_rows > 0);
1447 switch (n_selected_rows)
1450 may_delete_cases = FALSE;
1454 /* The row used for inserting new cases cannot be deleted. */
1455 path = gtk_tree_path_new_from_indices (
1456 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1457 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1459 gtk_tree_path_free (path);
1463 may_delete_cases = TRUE;
1467 gtk_widget_set_sensitive (dw->mi_clear_cases, may_delete_cases);
1469 any_variables_selected = FALSE;
1470 may_delete_vars = may_insert_vars = FALSE;
1471 list = pspp_sheet_selection_get_selected_columns (selection);
1473 for (iter = list; iter != NULL; iter = iter->next)
1475 PsppSheetViewColumn *column = iter->data;
1476 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1480 may_delete_vars = may_insert_vars = TRUE;
1481 any_variables_selected = TRUE;
1484 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1485 may_insert_vars = TRUE;
1489 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1490 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1492 gtk_widget_set_sensitive (dw->mi_insert_var, may_insert_vars);
1493 gtk_widget_set_sensitive (dw->mi_clear_variables, may_delete_vars);
1494 gtk_widget_set_sensitive (data_sheet->pu_sort_up, may_delete_vars);
1495 gtk_widget_set_sensitive (data_sheet->pu_sort_down, may_delete_vars);
1497 psppire_data_sheet_update_clip_actions (data_sheet);
1498 psppire_data_sheet_update_primary_selection (data_sheet,
1499 (n_selected_rows > 0
1500 && any_variables_selected));
1504 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1505 struct range_set **rowsp,
1506 struct range_set **colsp)
1508 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1509 PsppireDataStore *data_store = data_sheet->data_store;
1510 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1511 unsigned long n_cases;
1512 struct range_set *rows, *cols;
1515 if (data_store == NULL)
1517 n_cases = psppire_data_store_get_case_count (data_store);
1519 rows = pspp_sheet_selection_get_range_set (selection);
1520 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1521 if (range_set_is_empty (rows))
1523 range_set_destroy (rows);
1527 cols = range_set_create ();
1528 list = pspp_sheet_selection_get_selected_columns (selection);
1529 for (iter = list; iter != NULL; iter = iter->next)
1531 PsppSheetViewColumn *column = iter->data;
1532 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1535 range_set_set1 (cols, var_get_dict_index (var), 1);
1538 if (range_set_is_empty (cols))
1540 range_set_destroy (rows);
1541 range_set_destroy (cols);
1550 /* Insert a case at the selected row */
1552 psppire_data_sheet_insert_case (PsppireDataSheet *data_sheet)
1554 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1555 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1556 PsppireDataStore *data_store = data_sheet->data_store;
1557 struct range_set *selected = pspp_sheet_selection_get_range_set (selection);
1558 unsigned long row = range_set_scan (selected, 0);
1559 range_set_destroy (selected);
1561 if (row <= psppire_data_store_get_case_count (data_store))
1562 psppire_data_store_insert_new_case (data_store, row);
1566 psppire_data_sheet_insert_variable (PsppireDataSheet *data_sheet)
1568 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1569 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1570 PsppireDict *dict = data_sheet->data_store->dict;
1571 PsppSheetViewColumn *column;
1572 struct variable *var;
1577 list = pspp_sheet_selection_get_selected_columns (selection);
1580 column = list->data;
1583 var = g_object_get_data (G_OBJECT (column), "variable");
1584 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1585 if (psppire_dict_generate_name (dict, name, sizeof name))
1586 psppire_dict_insert_variable (dict, index, name);
1590 psppire_data_sheet_edit_clear_variables (PsppireDataSheet *data_sheet)
1592 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1593 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1594 PsppireDict *dict = data_sheet->data_store->dict;
1596 GList *list = pspp_sheet_selection_get_selected_columns (selection);
1600 list = g_list_reverse (list);
1601 for (iter = list; iter; iter = iter->next)
1603 PsppSheetViewColumn *column = iter->data;
1604 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1606 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1612 psppire_data_sheet_edit_copy (PsppireDataSheet *data_sheet)
1614 psppire_data_sheet_set_clip (data_sheet, FALSE);
1618 psppire_data_sheet_edit_cut (PsppireDataSheet *data_sheet)
1620 psppire_data_sheet_set_clip (data_sheet, TRUE);
1624 psppire_data_sheet_edit_paste (PsppireDataSheet *data_sheet)
1626 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
1627 GtkClipboard *clipboard =
1628 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1630 gtk_clipboard_request_contents (clipboard,
1631 gdk_atom_intern ("UTF8_STRING", TRUE),
1632 psppire_data_sheet_clip_received_cb,
1637 psppire_data_sheet_init (PsppireDataSheet *obj)
1639 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1641 obj->show_value_labels = FALSE;
1642 obj->show_case_numbers = TRUE;
1643 obj->may_create_vars = TRUE;
1644 obj->may_delete_vars = TRUE;
1646 obj->owns_primary_selection = FALSE;
1648 obj->scroll_to_bottom_signal = 0;
1649 obj->scroll_to_right_signal = 0;
1650 obj->on_owner_change_signal = 0;
1651 obj->new_variable_column = NULL;
1652 obj->container = NULL;
1654 obj->dispose_has_run = FALSE;
1656 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1659 obj->row_popup_menu = gtk_menu_new ();
1662 GtkWidget *insert_case = gtk_menu_item_new_with_mnemonic (_("_Insert Case"));
1663 GtkWidget *clear_cases = gtk_menu_item_new_with_mnemonic (_("Cl_ear Cases"));
1665 gtk_menu_attach (GTK_MENU (obj->row_popup_menu), insert_case, 0, 1, i, i + 1); ++i;
1666 gtk_menu_attach (GTK_MENU (obj->row_popup_menu), clear_cases, 0, 1, i, i + 1); ++i;
1668 g_signal_connect_swapped (clear_cases, "activate", G_CALLBACK (psppire_data_sheet_edit_clear_cases), obj);
1669 g_signal_connect_swapped (insert_case, "activate", G_CALLBACK (psppire_data_sheet_insert_case), obj);
1671 gtk_widget_show_all (obj->row_popup_menu);
1675 obj->column_popup_menu = gtk_menu_new ();
1678 GtkWidget *insert_variable = gtk_menu_item_new_with_mnemonic (_("_Insert Variable"));
1679 GtkWidget *clear_variables = gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables"));
1680 obj->pu_sort_up = gtk_menu_item_new_with_mnemonic (_("Sort _Ascending"));
1681 obj->pu_sort_down = gtk_menu_item_new_with_mnemonic (_("Sort _Descending"));
1683 g_signal_connect_swapped (clear_variables, "activate", G_CALLBACK (psppire_data_sheet_edit_clear_variables), obj);
1684 g_signal_connect_swapped (insert_variable, "activate", G_CALLBACK (psppire_data_sheet_insert_variable), obj);
1686 g_signal_connect_swapped (obj->pu_sort_up, "activate", G_CALLBACK (on_sort_up), obj);
1687 g_signal_connect_swapped (obj->pu_sort_down, "activate", G_CALLBACK (on_sort_down), obj);
1689 gtk_menu_attach (GTK_MENU (obj->column_popup_menu), insert_variable, 0, 1, i, i + 1); ++i;
1690 gtk_menu_attach (GTK_MENU (obj->column_popup_menu), clear_variables, 0, 1, i, i + 1); ++i;
1692 gtk_menu_attach (GTK_MENU (obj->column_popup_menu), gtk_separator_menu_item_new (), 0, 1, i, i + 1); ++i;
1694 gtk_menu_attach (GTK_MENU (obj->column_popup_menu), obj->pu_sort_up, 0, 1, i, i + 1); ++i;
1695 gtk_menu_attach (GTK_MENU (obj->column_popup_menu), obj->pu_sort_down, 0, 1, i, i + 1); ++i;
1697 gtk_widget_show_all (obj->column_popup_menu);
1701 g_signal_connect (obj, "notify::model",
1702 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1704 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1705 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1706 PSPP_SHEET_SELECTION_RECTANGLE);
1708 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1709 g_signal_connect (obj, "query-tooltip",
1710 G_CALLBACK (on_query_tooltip), NULL);
1711 g_signal_connect (obj, "button-press-event",
1712 G_CALLBACK (on_button_pressed), NULL);
1714 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1716 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1717 "changed", G_CALLBACK (on_selection_changed), NULL);
1721 psppire_data_sheet_new (void)
1723 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1727 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1729 return data_sheet->data_store;
1733 refresh_model (PsppireDataSheet *data_sheet)
1735 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1737 if (data_sheet->data_store != NULL)
1739 int n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1740 PsppireEmptyListStore *model = psppire_empty_list_store_new (n_rows);
1741 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1742 GTK_TREE_MODEL (model));
1743 g_object_unref (model);
1748 on_case_inserted (PsppireDataStore *data_store, gint row,
1749 PsppireDataSheet *data_sheet)
1751 PsppireEmptyListStore *empty_list_store;
1752 GtkTreeModel *tree_model;
1755 g_return_if_fail (data_store == data_sheet->data_store);
1757 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1758 if (row == n_rows - 1)
1761 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1762 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1763 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1764 psppire_empty_list_store_row_inserted (empty_list_store, row);
1768 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1769 PsppireDataSheet *data_sheet)
1772 g_return_if_fail (data_store == data_sheet->data_store);
1776 /* This is a bit of a cop-out. We could do better, if it ever turns out
1777 that this performs too poorly. */
1778 refresh_model (data_sheet);
1782 PsppireEmptyListStore *empty_list_store;
1783 GtkTreeModel *tree_model;
1784 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1786 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1787 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1788 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1789 psppire_empty_list_store_row_deleted (empty_list_store, first);
1794 on_case_change (PsppireDataStore *data_store, gint row,
1795 PsppireDataSheet *data_sheet)
1797 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1799 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1800 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1804 on_backend_changed (PsppireDataStore *data_store,
1805 PsppireDataSheet *data_sheet)
1807 g_return_if_fail (data_store == data_sheet->data_store);
1808 refresh_model (data_sheet);
1812 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1813 PsppireDataSheet *data_sheet)
1815 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1816 PsppSheetViewColumn *column;
1817 struct variable *var;
1821 g_return_if_fail (data_sheet->data_store != NULL);
1822 g_return_if_fail (dict == data_sheet->data_store->dict);
1824 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1829 var = psppire_dict_get_variable (data_store->dict, dict_index);
1830 g_return_if_fail (var != NULL);
1832 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1833 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1834 if (display_width != var_get_display_width (var))
1836 gint base_width, incr_width;
1838 display_width = var_get_display_width (var);
1839 calc_width_conversion (data_sheet, &base_width, &incr_width);
1840 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1841 base_width, incr_width);
1842 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1847 on_variable_changed (PsppireDict *dict, int dict_index,
1848 guint what, const struct variable *oldvar,
1849 PsppireDataSheet *data_sheet)
1851 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1852 PsppSheetViewColumn *column;
1853 GtkCellRenderer *cell;
1854 struct variable *var;
1858 g_return_if_fail (data_sheet->data_store != NULL);
1859 g_return_if_fail (dict == data_sheet->data_store->dict);
1862 if (what & VAR_TRAIT_DISPLAY_WIDTH)
1863 on_variable_display_width_changed (dict, dict_index, data_sheet);
1865 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1871 var = psppire_dict_get_variable (data_store->dict, dict_index);
1872 g_return_if_fail (var != NULL);
1874 name = escape_underscores (var_get_name (var));
1875 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1876 pspp_sheet_view_column_set_title (column, name);
1879 cells = pspp_sheet_view_column_get_cell_renderers (column);
1880 g_return_if_fail (cells);
1882 g_list_free (cells);
1884 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1886 /* Stop editing before we delete and replace the cell renderers.
1887 Otherwise if this column is currently being edited, an eventual call
1888 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1889 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1891 It's possible that this is a bug in PsppSheetView, and it's possible
1892 that PsppSheetView inherits that from GtkTreeView, but I haven't
1893 investigated yet. */
1894 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1896 add_data_column_cell_renderer (data_sheet, column);
1901 on_variable_inserted (PsppireDict *dict, int var_index,
1902 PsppireDataSheet *data_sheet)
1904 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1905 gint base_width, incr_width;
1906 PsppSheetViewColumn *column;
1908 calc_width_conversion (data_sheet, &base_width, &incr_width);
1909 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1910 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1914 on_variable_deleted (PsppireDict *dict,
1915 const struct variable *var, int case_idx, int width,
1916 PsppireDataSheet *data_sheet)
1918 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1919 GList *columns, *iter;
1921 columns = pspp_sheet_view_get_columns (sheet_view);
1922 for (iter = columns; iter != NULL; iter = iter->next)
1924 PsppSheetViewColumn *column = iter->data;
1925 const struct variable *column_var;
1927 column_var = g_object_get_data (G_OBJECT (column), "variable");
1928 if (column_var == var)
1929 pspp_sheet_view_remove_column (sheet_view, column);
1931 g_list_free (columns);
1935 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1937 PsppireDataStore *store = data_sheet->data_store;
1942 data_sheet->data_store = NULL;
1944 g_signal_handlers_disconnect_by_func (
1945 store, G_CALLBACK (on_backend_changed), data_sheet);
1946 g_signal_handlers_disconnect_by_func (
1947 store, G_CALLBACK (on_case_inserted), data_sheet);
1948 g_signal_handlers_disconnect_by_func (
1949 store, G_CALLBACK (on_cases_deleted), data_sheet);
1950 g_signal_handlers_disconnect_by_func (
1951 store, G_CALLBACK (on_case_change), data_sheet);
1953 g_signal_handlers_disconnect_by_func (
1954 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
1955 g_signal_handlers_disconnect_by_func (
1956 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
1957 g_signal_handlers_disconnect_by_func (
1958 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
1959 g_signal_handlers_disconnect_by_func (
1960 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
1962 g_object_unref (store);
1966 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
1967 PsppireDataStore *data_store)
1969 psppire_data_sheet_unset_data_store (data_sheet);
1971 data_sheet->data_store = data_store;
1972 if (data_store != NULL)
1974 g_object_ref (data_store);
1975 g_signal_connect (data_store, "backend-changed",
1976 G_CALLBACK (on_backend_changed), data_sheet);
1977 g_signal_connect (data_store, "case-inserted",
1978 G_CALLBACK (on_case_inserted), data_sheet);
1979 g_signal_connect (data_store, "cases-deleted",
1980 G_CALLBACK (on_cases_deleted), data_sheet);
1981 g_signal_connect (data_store, "case-changed",
1982 G_CALLBACK (on_case_change), data_sheet);
1984 /* XXX it's unclean to hook into the dict this way--what if the dict
1985 changes? As of this writing, though, nothing ever changes the
1986 data_store's dict. */
1987 g_signal_connect (data_store->dict, "variable-changed",
1988 G_CALLBACK (on_variable_changed),
1990 g_signal_connect (data_store->dict, "variable-inserted",
1991 G_CALLBACK (on_variable_inserted), data_sheet);
1992 g_signal_connect (data_store->dict, "variable-deleted",
1993 G_CALLBACK (on_variable_deleted), data_sheet);
1995 refresh_model (data_sheet);
1998 /* Clipboard stuff */
2000 /* A casereader and dictionary holding the data currently in the clip */
2001 static struct casereader *clip_datasheet = NULL;
2002 static struct dictionary *clip_dict = NULL;
2005 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2008 psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
2009 struct casereader **readerp,
2010 struct dictionary **dictp)
2012 struct casewriter *writer ;
2013 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2014 struct case_map *map = NULL;
2015 struct range_set *rows, *cols;
2016 const struct range_set_node *node;
2017 struct dictionary *dict;
2019 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2026 /* Construct clip dictionary. */
2027 *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
2028 RANGE_SET_FOR_EACH (node, cols)
2032 for (dict_index = node->start; dict_index < node->end; dict_index++)
2034 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2035 dict_clone_var_assert (dict, var);
2039 /* Construct clip data. */
2040 map = case_map_by_name (ds->dict->dict, dict);
2041 writer = autopaging_writer_create (dict_get_proto (dict));
2042 RANGE_SET_FOR_EACH (node, rows)
2044 unsigned long int row;
2046 for (row = node->start; row < node->end; row++)
2048 struct ccase *old = psppire_data_store_get_case (ds, row);
2050 casewriter_write (writer, case_map_execute (map, old));
2052 casewriter_force_error (writer);
2055 case_map_destroy (map);
2057 /* Clear data that we copied out, if we're doing a "cut". */
2058 if (cut && !casewriter_error (writer))
2060 RANGE_SET_FOR_EACH (node, rows)
2062 unsigned long int row;
2064 for (row = node->start; row < node->end; row++)
2066 const struct range_set_node *node2;
2068 RANGE_SET_FOR_EACH (node2, cols)
2072 for (dict_index = node2->start; dict_index < node2->end;
2075 struct variable *var;
2077 var = dict_get_var (ds->dict->dict, dict_index);
2078 psppire_data_store_set_string (ds, "", row,
2086 range_set_destroy (rows);
2087 range_set_destroy (cols);
2089 *readerp = casewriter_make_reader (writer);
2094 /* Set the clip from the currently selected range in DATA_SHEET. If CUT is
2095 true, clears the original data from DATA_SHEET, otherwise leaves the
2096 original data in-place. */
2098 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2101 struct casereader *reader;
2102 struct dictionary *dict;
2104 if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
2106 casereader_destroy (clip_datasheet);
2107 dict_destroy (clip_dict);
2109 clip_datasheet = reader;
2112 psppire_data_sheet_update_clipboard (data_sheet);
2123 /* Perform data_out for case CC, variable V, appending to STRING */
2125 data_out_g_string (GString *string, const struct variable *v,
2126 const struct ccase *cc)
2128 const struct fmt_spec *fs = var_get_print_format (v);
2129 const union value *val = case_data (cc, v);
2131 char *s = data_out (val, var_get_encoding (v), fs);
2133 g_string_append (string, s);
2139 clip_to_text (struct casereader *datasheet, struct dictionary *dict)
2144 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2145 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2146 const size_t var_cnt = dict_get_var_cnt (dict);
2148 string = g_string_sized_new (10 * val_cnt * case_cnt);
2150 for (r = 0 ; r < case_cnt ; ++r )
2155 cc = casereader_peek (datasheet, r);
2158 g_warning ("Clipboard seems to have inexplicably shrunk");
2162 for (c = 0 ; c < var_cnt ; ++c)
2164 const struct variable *v = dict_get_var (dict, c);
2165 data_out_g_string (string, v, cc);
2166 if ( c < val_cnt - 1 )
2167 g_string_append (string, "\t");
2171 g_string_append (string, "\n");
2181 clip_to_html (struct casereader *datasheet, struct dictionary *dict)
2186 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2187 const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2188 const size_t var_cnt = dict_get_var_cnt (dict);
2190 /* Guestimate the size needed */
2191 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2193 g_string_append (string,
2194 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2196 g_string_append (string, "<table>\n");
2197 for (r = 0 ; r < case_cnt ; ++r )
2200 struct ccase *cc = casereader_peek (datasheet, r);
2203 g_warning ("Clipboard seems to have inexplicably shrunk");
2206 g_string_append (string, "<tr>\n");
2208 for (c = 0 ; c < var_cnt ; ++c)
2210 const struct variable *v = dict_get_var (dict, c);
2211 g_string_append (string, "<td>");
2212 data_out_g_string (string, v, cc);
2213 g_string_append (string, "</td>\n");
2216 g_string_append (string, "</tr>\n");
2220 g_string_append (string, "</table>\n");
2228 psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
2230 struct casereader *reader,
2231 struct dictionary *dict)
2233 GString *string = NULL;
2237 case SELECT_FMT_TEXT:
2238 string = clip_to_text (reader, dict);
2240 case SELECT_FMT_HTML:
2241 string = clip_to_html (reader, dict);
2244 g_assert_not_reached ();
2247 gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
2249 (const guchar *) string->str, string->len);
2251 g_string_free (string, TRUE);
2255 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2256 GtkSelectionData *selection_data,
2260 psppire_data_sheet_clipboard_set (selection_data, info,
2261 clip_datasheet, clip_dict);
2265 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2268 dict_destroy (clip_dict);
2271 casereader_destroy (clip_datasheet);
2272 clip_datasheet = NULL;
2276 static const GtkTargetEntry targets[] = {
2277 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2278 { "STRING", 0, SELECT_FMT_TEXT },
2279 { "TEXT", 0, SELECT_FMT_TEXT },
2280 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2281 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2282 { "text/plain", 0, SELECT_FMT_TEXT },
2283 { "text/html", 0, SELECT_FMT_HTML }
2289 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2291 GtkClipboard *clipboard =
2292 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2293 GDK_SELECTION_CLIPBOARD);
2295 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2296 G_N_ELEMENTS (targets),
2297 psppire_data_sheet_clipboard_get_cb,
2298 psppire_data_sheet_clipboard_clear_cb,
2300 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2304 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2306 struct range_set *rows, *cols;
2307 GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (data_sheet));
2308 if (! PSPPIRE_IS_DATA_WINDOW (top))
2311 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top);
2313 psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2317 range_set_destroy (rows);
2318 range_set_destroy (cols);
2321 gtk_widget_set_sensitive (dw->mi_copy, enable);
2322 gtk_widget_set_sensitive (dw->mi_cut, enable);
2326 psppire_data_sheet_primary_get_cb (GtkClipboard *clipboard,
2327 GtkSelectionData *selection_data,
2331 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2332 struct casereader *reader;
2333 struct dictionary *dict;
2335 if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
2337 psppire_data_sheet_clipboard_set (selection_data, info,
2339 casereader_destroy (reader);
2340 dict_destroy (dict);
2345 psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
2346 gboolean should_own)
2348 GtkClipboard *clipboard;
2349 GdkDisplay *display;
2351 display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
2352 clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
2353 g_return_if_fail (clipboard != NULL);
2355 if (data_sheet->owns_primary_selection && !should_own)
2357 data_sheet->owns_primary_selection = FALSE;
2358 gtk_clipboard_clear (clipboard);
2360 else if (should_own)
2361 data_sheet->owns_primary_selection =
2362 gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2363 psppire_data_sheet_primary_get_cb,
2364 NULL, G_OBJECT (data_sheet));
2367 /* A callback for when the clipboard contents have been received. */
2369 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2370 GtkSelectionData *sd,
2373 PsppireDataSheet *data_sheet = data;
2374 PsppireDataStore *store = data_sheet->data_store;
2375 struct range_set *rows, *cols;
2377 gint next_row, next_column;
2381 if ( gtk_selection_data_get_length (sd) < 0 )
2384 if ( gtk_selection_data_get_data_type (sd) != gdk_atom_intern ("UTF8_STRING", FALSE))
2387 c = (char *) gtk_selection_data_get_data (sd);
2389 /* Get the starting selected position in the data sheet. (Possibly we should
2390 only paste into the selected range if it's larger than one cell?) */
2391 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2393 next_row = range_set_first (rows)->start;
2394 first_column = next_column = range_set_first (cols)->start;
2395 range_set_destroy (rows);
2396 range_set_destroy (cols);
2398 g_return_if_fail (next_row >= 0);
2399 g_return_if_fail (next_column >= 0);
2401 while (count < gtk_selection_data_get_length (sd))
2403 gint row = next_row;
2404 gint column = next_column;
2405 struct variable *var;
2408 while (*c != '\t' && *c != '\n' && count < gtk_selection_data_get_length (sd))
2416 next_column = column + 1;
2418 else if ( *c == '\n')
2421 next_column = first_column;
2426 var = psppire_dict_get_variable (store->dict, column);
2428 psppire_data_store_set_string (store, s, row, var, FALSE);
2433 psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
2438 GtkWidget *mi = GTK_WIDGET (data);
2439 gboolean compatible_target = FALSE;
2441 for (i = 0; i < G_N_ELEMENTS (targets); i++)
2443 GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
2446 for (j = 0; j < n_atoms; j++)
2447 if (target == atoms[j])
2449 compatible_target = TRUE;
2454 gtk_widget_set_sensitive (mi, compatible_target);
2458 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2460 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2462 GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (data_sheet));
2463 if (! PSPPIRE_IS_DATA_WINDOW (top))
2466 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top);
2468 gtk_clipboard_request_targets (clip,
2469 psppire_data_sheet_targets_received_cb,