1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012 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_set_clip (PsppireDataSheet *, gboolean cut);
56 static void on_selection_changed (PsppSheetSelection *, gpointer);
57 static void on_owner_change (GtkClipboard *, GdkEventOwnerChange *, gpointer);
58 static void psppire_data_sheet_clip_received_cb (GtkClipboard *,
59 GtkSelectionData *, gpointer);
61 G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, PSPP_TYPE_SHEET_VIEW);
64 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
66 size_t *row, PsppSheetViewColumn **columnp)
68 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
72 PsppSheetViewColumn *tree_column;
73 GtkTreeModel *tree_model;
76 /* Check that WIDGET is really visible on the screen before we
77 do anything else. This is a bug fix for a sticky situation:
78 when text_data_import_assistant() returns, it frees the data
79 necessary to compose the tool tip message, but there may be
80 a tool tip under preparation at that point (even if there is
81 no visible tool tip) that will call back into us a little
82 bit later. Perhaps the correct solution to this problem is
83 to make the data related to the tool tips part of a GObject
84 that only gets destroyed when all references are released,
85 but this solution appears to be effective too. */
86 if (!gtk_widget_get_mapped (widget))
89 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
91 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
92 &path, &tree_column, NULL, NULL))
95 *columnp = tree_column;
97 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
100 tree_model = pspp_sheet_view_get_model (tree_view);
101 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
102 gtk_tree_path_free (path);
106 *row = GPOINTER_TO_INT (iter.user_data);
111 on_query_tooltip (GtkWidget *widget, gint wx, gint wy,
112 gboolean keyboard_mode UNUSED,
113 GtkTooltip *tooltip, gpointer data UNUSED)
115 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
116 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
117 PsppSheetViewColumn *column;
118 struct variable *var;
124 g_return_val_if_fail (data_store != NULL, FALSE);
126 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
129 var = g_object_get_data (G_OBJECT (column), "variable");
132 if (g_object_get_data (G_OBJECT (column), "new-var-column") == NULL)
135 gtk_tooltip_set_text (tooltip,
136 _("Enter a number to add a new variable."));
139 else if (row >= datasheet_get_n_rows (data_store->datasheet))
141 gtk_tooltip_set_text (tooltip, _("Enter a number to add a new case."));
145 width = var_get_width (var);
147 value_init (&v, width);
148 datasheet_get_value (data_store->datasheet, row, var_get_case_index (var),
151 label = var_lookup_value_label (var, &v);
154 if (data_sheet->show_value_labels)
156 char *s = value_to_text (v, var);
157 gtk_tooltip_set_text (tooltip, s);
161 gtk_tooltip_set_text (tooltip, label);
163 value_destroy (&v, width);
165 return label != NULL;
169 render_row_number_cell (PsppSheetViewColumn *tree_column,
170 GtkCellRenderer *cell,
175 PsppireDataStore *store = store_;
176 GValue gvalue = { 0, };
179 row = GPOINTER_TO_INT (iter->user_data);
181 g_value_init (&gvalue, G_TYPE_INT);
182 g_value_set_int (&gvalue, row + 1);
183 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
184 g_value_unset (&gvalue);
186 if (row < datasheet_get_n_rows (store->datasheet))
187 g_object_set (cell, "editable", TRUE, NULL);
189 g_object_set (cell, "editable", FALSE, NULL);
192 "slash", psppire_data_store_filtered (store, row),
197 on_row_number_clicked (PsppireCellRendererButton *button,
199 PsppSheetView *sheet_view)
201 PsppSheetSelection *selection;
204 path = gtk_tree_path_new_from_string (path_string);
206 selection = pspp_sheet_view_get_selection (sheet_view);
207 pspp_sheet_selection_unselect_all (selection);
208 pspp_sheet_selection_select_path (selection, path);
209 pspp_sheet_selection_select_all_columns (selection);
211 gtk_tree_path_free (path);
215 make_row_number_column (PsppireDataSheet *data_sheet,
216 PsppireDataStore *ds)
218 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
219 PsppSheetViewColumn *column;
220 GtkCellRenderer *renderer;
222 renderer = psppire_cell_renderer_button_new ();
223 g_object_set (renderer, "xalign", 1.0, NULL);
224 g_signal_connect (renderer, "clicked", G_CALLBACK (on_row_number_clicked),
227 column = pspp_sheet_view_column_new_with_attributes (_("Case"),
229 pspp_sheet_view_column_set_selectable (column, TRUE);
230 pspp_sheet_view_column_set_row_head (column, TRUE);
231 pspp_sheet_view_column_set_tabbable (column, FALSE);
232 pspp_sheet_view_column_set_clickable (column, TRUE);
233 pspp_sheet_view_column_set_cell_data_func (
234 column, renderer, render_row_number_cell, ds, NULL);
235 pspp_sheet_view_column_set_fixed_width (column, 50);
236 pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers);
237 pspp_sheet_view_append_column (sheet_view, column);
241 render_data_cell (PsppSheetViewColumn *tree_column,
242 GtkCellRenderer *cell,
245 gpointer data_sheet_)
247 PsppireDataSheet *data_sheet = data_sheet_;
248 PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet);
249 struct variable *var;
255 row = GPOINTER_TO_INT (iter->user_data);
256 var = g_object_get_data (G_OBJECT (tree_column), "variable");
258 string = psppire_data_store_get_string (store, row, var,
259 data_sheet->show_value_labels);
262 GValue gvalue = { 0 };
264 g_value_init (&gvalue, G_TYPE_STRING);
265 g_value_take_string (&gvalue, string);
266 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
267 g_value_unset (&gvalue);
270 g_object_set (G_OBJECT (cell), "text", "", NULL);
272 switch (var_get_alignment (var))
274 case ALIGN_LEFT: xalign = 0.0; break;
275 case ALIGN_RIGHT: xalign = 1.0; break;
276 case ALIGN_CENTRE: xalign = 0.5; break;
277 default: xalign = 0.0; break;
286 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
290 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
291 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
292 NULL, NULL, NULL, &width, NULL);
297 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
304 ds_put_byte_multiple (&s, '0', char_cnt);
305 ds_put_byte (&s, ' ');
306 width = get_string_width (treeview, renderer, ds_cstr (&s));
313 on_data_column_editing_started (GtkCellRenderer *cell,
314 GtkCellEditable *editable,
318 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
319 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
320 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
321 struct variable *var;
323 g_return_if_fail (column);
324 g_return_if_fail (data_sheet);
325 g_return_if_fail (data_store);
328 g_object_ref (editable);
329 g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable",
330 editable, g_object_unref);
332 var = g_object_get_data (G_OBJECT (column), "variable");
333 g_return_if_fail (var);
335 if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable))
337 const struct val_labs *labels = var_get_value_labels (var);
338 const struct val_lab *vl;
339 GtkListStore *list_store;
341 list_store = gtk_list_store_new (1, G_TYPE_STRING);
342 for (vl = val_labs_first (labels); vl != NULL;
343 vl = val_labs_next (labels, vl))
347 gtk_list_store_append (list_store, &iter);
348 gtk_list_store_set (list_store, &iter,
349 0, val_lab_get_label (vl),
353 gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
354 GTK_TREE_MODEL (list_store));
355 g_object_unref (list_store);
360 scroll_to_bottom (GtkWidget *widget,
361 GtkRequisition *requisition,
362 gpointer unused UNUSED)
364 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
365 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
366 GtkAdjustment *vadjust;
368 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
369 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
371 if (data_sheet->scroll_to_bottom_signal)
373 g_signal_handler_disconnect (data_sheet,
374 data_sheet->scroll_to_bottom_signal);
375 data_sheet->scroll_to_bottom_signal = 0;
380 on_data_column_edited (GtkCellRendererText *cell,
385 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
386 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
387 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
388 GtkEditable *editable;
389 struct variable *var;
395 path = gtk_tree_path_new_from_string (path_string);
396 row = gtk_tree_path_get_indices (path)[0];
397 gtk_tree_path_free (path);
399 var = g_object_get_data (G_OBJECT (column), "variable");
401 new_row = row == psppire_data_store_get_case_count (data_store);
402 if (new_row && new_text[0] == '\0')
405 editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
406 g_return_if_fail (editable != NULL);
407 is_val_lab = (GTK_IS_COMBO_BOX (editable)
408 && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
409 g_object_unref (editable);
411 psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
413 if (new_row && !data_sheet->scroll_to_bottom_signal)
415 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
416 data_sheet->scroll_to_bottom_signal =
417 g_signal_connect (data_sheet, "size-request",
418 G_CALLBACK (scroll_to_bottom), NULL);
422 /* We could be more specific about what to redraw, if it seems
423 important for performance. */
424 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
429 scroll_to_right (GtkWidget *widget,
430 PsppireDataSheet *data_sheet)
432 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
433 PsppSheetViewColumn *column, *prev;
434 GList *columns, *iter;
438 columns = pspp_sheet_view_get_columns (sheet_view);
439 for (iter = columns; iter; iter = iter->next)
441 PsppSheetViewColumn *c = iter->data;
442 if (g_object_get_data (G_OBJECT (c), "new-var-column"))
449 g_list_free (columns);
454 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
460 pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
463 pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
464 gtk_tree_path_free (path);
468 if (data_sheet->scroll_to_right_signal)
470 g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
471 data_sheet->scroll_to_right_signal = 0;
476 on_new_variable_column_edited (GtkCellRendererText *cell,
481 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
482 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
483 PsppireDict *dict = data_store->dict;
484 struct variable *var;
489 if (new_text[0] == '\0')
491 /* User didn't enter anything so don't create a variable. */
495 path = gtk_tree_path_new_from_string (path_string);
496 row = gtk_tree_path_get_indices (path)[0];
497 gtk_tree_path_free (path);
499 if (!psppire_dict_generate_name (dict, name, sizeof name))
502 var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
504 g_return_if_fail (var != NULL);
506 psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
508 if (!data_sheet->scroll_to_right_signal)
510 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
511 data_sheet->scroll_to_right_signal =
512 g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
513 G_CALLBACK (scroll_to_right), data_sheet);
517 /* We could be more specific about what to redraw, if it seems
518 important for performance. */
519 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
524 calc_width_conversion (PsppireDataSheet *data_sheet,
525 gint *base_width, gint *incr_width)
527 GtkCellRenderer *cell;
530 cell = gtk_cell_renderer_text_new ();
531 w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
532 w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
533 *incr_width = MAX (1, (w10 - w1) / 9);
534 *base_width = MAX (0, w10 - *incr_width * 10);
535 g_object_ref_sink (cell);
536 g_object_unref (cell);
540 display_width_from_pixel_width (PsppireDataSheet *data_sheet,
543 gint base_width, incr_width;
545 calc_width_conversion (data_sheet, &base_width, &incr_width);
546 return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
550 display_width_to_pixel_width (PsppireDataSheet *data_sheet,
555 return base_width + incr_width * display_width;
559 on_data_column_resized (GObject *gobject,
563 PsppireDataSheet *data_sheet = user_data;
564 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
565 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
566 struct variable *var;
570 if (data_store == NULL)
573 pixel_width = pspp_sheet_view_column_get_width (column);
574 if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
576 /* Short-circuit the expensive display_width_from_pixel_width()
577 calculation, to make loading .sav files with 2000 columns visibly
582 var = g_object_get_data (G_OBJECT (column), "variable");
583 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
584 var_set_display_width (var, display_width);
588 do_data_column_popup_menu (PsppSheetViewColumn *column,
589 guint button, guint32 time)
591 GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
592 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
595 menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
596 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
600 on_data_column_popup_menu (PsppSheetViewColumn *column,
601 gpointer user_data UNUSED)
603 do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
607 on_column_button_press_event (PsppSheetViewColumn *column,
608 GdkEventButton *event,
609 gpointer user_data UNUSED)
611 PsppSheetSelection *selection;
612 PsppSheetView *sheet_view;
614 sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
616 g_return_val_if_fail (sheet_view != NULL, FALSE);
618 selection = pspp_sheet_view_get_selection (sheet_view);
619 g_return_val_if_fail (selection != NULL, FALSE);
621 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
623 do_data_column_popup_menu (column, event->button, event->time);
626 else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
628 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
629 struct variable *var;
631 var = g_object_get_data (G_OBJECT (column), "variable");
636 g_signal_emit_by_name (data_sheet, "var-double-clicked",
637 var_get_dict_index (var), &handled);
646 on_data_column_query_tooltip (PsppSheetViewColumn *column,
648 gpointer user_data UNUSED)
650 struct variable *var;
653 var = g_object_get_data (G_OBJECT (column), "variable");
654 g_return_val_if_fail (var != NULL, FALSE);
656 text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
657 gtk_tooltip_set_text (tooltip, text);
663 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
664 PsppSheetViewColumn *column)
666 GtkCellRenderer *cell;
667 struct variable *var;
669 var = g_object_get_data (G_OBJECT (column), "variable");
670 g_return_if_fail (var != NULL);
672 if (var_has_value_labels (var))
674 cell = gtk_cell_renderer_combo_new ();
675 g_object_set (G_OBJECT (cell),
681 cell = gtk_cell_renderer_text_new ();
683 g_signal_connect (cell, "editing-started",
684 G_CALLBACK (on_data_column_editing_started), NULL);
685 g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
687 g_object_set_data (G_OBJECT (cell), "column", column);
688 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
690 pspp_sheet_view_column_clear (column);
691 pspp_sheet_view_column_pack_start (column, cell, TRUE);
693 pspp_sheet_view_column_set_cell_data_func (
694 column, cell, render_data_cell, data_sheet, NULL);
697 static PsppSheetViewColumn *
698 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
699 gint base_width, gint incr_width)
701 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
702 struct variable *var;
703 PsppSheetViewColumn *column;
707 var = psppire_dict_get_variable (data_store->dict, dict_idx);
709 column = pspp_sheet_view_column_new ();
711 name = escape_underscores (var_get_name (var));
712 pspp_sheet_view_column_set_title (column, name);
715 g_object_set_data (G_OBJECT (column), "variable", var);
717 width = display_width_to_pixel_width (data_sheet,
718 var_get_display_width (var),
719 base_width, incr_width);
720 pspp_sheet_view_column_set_min_width (column, 10);
721 pspp_sheet_view_column_set_fixed_width (column, width);
722 pspp_sheet_view_column_set_resizable (column, TRUE);
724 pspp_sheet_view_column_set_clickable (column, TRUE);
725 g_signal_connect (column, "notify::width",
726 G_CALLBACK (on_data_column_resized), data_sheet);
728 g_signal_connect (column, "button-press-event",
729 G_CALLBACK (on_column_button_press_event),
731 g_signal_connect (column, "query-tooltip",
732 G_CALLBACK (on_data_column_query_tooltip), NULL);
733 g_signal_connect (column, "popup-menu",
734 G_CALLBACK (on_data_column_popup_menu), data_sheet);
736 add_data_column_cell_renderer (data_sheet, column);
742 make_new_variable_column (PsppireDataSheet *data_sheet,
743 gint base_width, gint incr_width)
745 PsppSheetViewColumn *column;
746 GtkCellRenderer *cell;
749 cell = gtk_cell_renderer_text_new ();
750 g_object_set (cell, "editable", TRUE, NULL);
752 g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
755 column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
756 g_object_set_data (G_OBJECT (column), "new-var-column", column);
758 width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
759 pspp_sheet_view_column_set_min_width (column, 10);
760 pspp_sheet_view_column_set_fixed_width (column, width);
761 pspp_sheet_view_column_set_tabbable (column, FALSE);
763 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
764 g_signal_connect (column, "button-press-event",
765 G_CALLBACK (on_column_button_press_event),
767 g_signal_connect (column, "popup-menu",
768 G_CALLBACK (on_data_column_popup_menu), data_sheet);
770 pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
772 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
773 data_sheet->new_variable_column = column;
777 psppire_data_sheet_model_changed (GObject *gobject,
781 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
782 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
783 PsppireDataStore *data_store;
785 /* Remove old columns. */
788 PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
792 pspp_sheet_view_remove_column (sheet_view, column);
794 data_sheet->new_variable_column = NULL;
796 if (pspp_sheet_view_get_model (sheet_view) == NULL)
798 /* Don't create any columns at all if there's no model. Otherwise we'll
799 create some columns as part of the "dispose" callback for the sheet
800 view, which sets the model to NULL. That causes warnings to be
801 logged and is obviously undesirable in any case. */
805 /* Add new columns. */
806 data_store = psppire_data_sheet_get_data_store (data_sheet);
807 if (data_store != NULL)
809 gint base_width, incr_width;
812 calc_width_conversion (data_sheet, &base_width, &incr_width);
814 make_row_number_column (data_sheet, data_store);
815 for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
817 PsppSheetViewColumn *column;
819 column = make_data_column (data_sheet, i, base_width, incr_width);
820 pspp_sheet_view_append_column (sheet_view, column);
822 make_new_variable_column (data_sheet, base_width, incr_width);
833 PROP_MAY_CREATE_VARS,
834 PROP_MAY_DELETE_VARS,
839 psppire_data_sheet_set_property (GObject *object,
844 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
848 case PROP_DATA_STORE:
849 psppire_data_sheet_set_data_store (
850 obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
853 case PROP_VALUE_LABELS:
854 psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
857 case PROP_CASE_NUMBERS:
858 psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
861 case PROP_CURRENT_CASE:
862 psppire_data_sheet_goto_case (obj, g_value_get_long (value));
865 case PROP_MAY_CREATE_VARS:
866 psppire_data_sheet_set_may_create_vars (obj,
867 g_value_get_boolean (value));
870 case PROP_MAY_DELETE_VARS:
871 psppire_data_sheet_set_may_delete_vars (obj,
872 g_value_get_boolean (value));
875 case PROP_UI_MANAGER:
877 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
883 psppire_data_sheet_get_property (GObject *object,
888 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
892 case PROP_DATA_STORE:
893 g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
896 case PROP_VALUE_LABELS:
897 g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
900 case PROP_CASE_NUMBERS:
901 g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
904 case PROP_CURRENT_CASE:
905 g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
908 case PROP_MAY_CREATE_VARS:
909 g_value_set_boolean (value, obj->may_create_vars);
912 case PROP_MAY_DELETE_VARS:
913 g_value_set_boolean (value, obj->may_delete_vars);
916 case PROP_UI_MANAGER:
917 g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
921 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
927 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
929 return ds->show_value_labels;
933 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
934 gboolean show_value_labels)
936 show_value_labels = !!show_value_labels;
937 if (show_value_labels != ds->show_value_labels)
939 ds->show_value_labels = show_value_labels;
940 g_object_notify (G_OBJECT (ds), "value-labels");
941 gtk_widget_queue_draw (GTK_WIDGET (ds));
943 /* Make the cell being edited refresh too. */
944 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (ds), TRUE);
949 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
951 return ds->show_case_numbers;
955 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
956 gboolean show_case_numbers)
958 show_case_numbers = !!show_case_numbers;
959 if (show_case_numbers != ds->show_case_numbers)
961 PsppSheetViewColumn *column;
963 ds->show_case_numbers = show_case_numbers;
964 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
966 pspp_sheet_view_column_set_visible (column, show_case_numbers);
968 g_object_notify (G_OBJECT (ds), "case-numbers");
969 gtk_widget_queue_draw (GTK_WIDGET (ds));
974 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
976 return data_sheet->may_create_vars;
980 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
981 gboolean may_create_vars)
983 if (data_sheet->may_create_vars != may_create_vars)
985 data_sheet->may_create_vars = may_create_vars;
986 if (data_sheet->new_variable_column)
987 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
990 on_selection_changed (pspp_sheet_view_get_selection (
991 PSPP_SHEET_VIEW (data_sheet)), NULL);
996 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
998 return data_sheet->may_delete_vars;
1002 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
1003 gboolean may_delete_vars)
1005 if (data_sheet->may_delete_vars != may_delete_vars)
1007 data_sheet->may_delete_vars = may_delete_vars;
1008 on_selection_changed (pspp_sheet_view_get_selection (
1009 PSPP_SHEET_VIEW (data_sheet)), NULL);
1013 static PsppSheetViewColumn *
1014 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1017 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1018 PsppireDataStore *data_store;
1019 PsppSheetViewColumn *column;
1020 struct variable *var;
1023 data_store = psppire_data_sheet_get_data_store (data_sheet);
1024 g_return_val_if_fail (data_store != NULL, NULL);
1025 g_return_val_if_fail (data_store->dict != NULL, NULL);
1027 var = psppire_dict_get_variable (data_store->dict, dict_index);
1028 g_return_val_if_fail (var != NULL, NULL);
1031 list = pspp_sheet_view_get_columns (sheet_view);
1032 for (iter = list; iter != NULL; iter = iter->next)
1034 PsppSheetViewColumn *c = iter->data;
1037 v = g_object_get_data (G_OBJECT (c), "variable");
1050 psppire_data_sheet_show_variable (PsppireDataSheet *data_sheet,
1053 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1054 PsppSheetViewColumn *column;
1056 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1059 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column,
1064 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1066 PsppSheetSelection *selection;
1067 struct variable *var;
1068 GList *selected_columns;
1071 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1072 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1075 for (iter = selected_columns; iter != NULL; iter = iter->next)
1077 PsppSheetViewColumn *column = iter->data;
1078 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1091 g_list_free (selected_columns);
1097 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1099 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1100 PsppireDataStore *store = data_sheet->data_store;
1101 PsppSheetSelection *selection;
1104 g_return_if_fail (case_index >= 0);
1105 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1107 path = gtk_tree_path_new_from_indices (case_index, -1);
1109 /* Select the case. */
1110 selection = pspp_sheet_view_get_selection (sheet_view);
1111 pspp_sheet_selection_unselect_all (selection);
1112 pspp_sheet_selection_select_path (selection, path);
1113 pspp_sheet_selection_select_all_columns (selection);
1115 /* Scroll so that the case is visible. */
1116 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1118 gtk_tree_path_free (path);
1121 /* Returns the 0-based index of a selected case, if there is at least one, and
1124 If more than one case is selected, returns the one with the smallest index,
1125 that is, the index of the case closest to the beginning of the file. The
1126 row that can be used to insert a new case is not considered a case. */
1128 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1130 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1131 PsppireDataStore *store = data_sheet->data_store;
1132 const struct range_set_node *node;
1133 PsppSheetSelection *selection;
1134 struct range_set *rows;
1137 selection = pspp_sheet_view_get_selection (sheet_view);
1138 rows = pspp_sheet_selection_get_range_set (selection);
1139 node = range_set_first (rows);
1140 row = (node && node->start < psppire_data_store_get_case_count (store)
1143 range_set_destroy (rows);
1148 /* Returns the 0-based index of a selected case, if exactly one case is
1149 selected, and -1 otherwise. Returns -1 if the row that can be used to
1150 insert a new case is selected. */
1152 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1154 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1155 PsppireDataStore *store = data_sheet->data_store;
1156 const struct range_set_node *node;
1157 PsppSheetSelection *selection;
1158 struct range_set *rows;
1161 selection = pspp_sheet_view_get_selection (sheet_view);
1162 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1165 rows = pspp_sheet_selection_get_range_set (selection);
1166 node = range_set_first (rows);
1167 row = (node && node->start < psppire_data_store_get_case_count (store)
1170 range_set_destroy (rows);
1176 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1178 if (data_sheet->uim == NULL)
1181 GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1183 GTK_TYPE_UI_MANAGER));
1184 g_object_ref (data_sheet->uim);
1187 return data_sheet->uim;
1191 psppire_data_sheet_dispose (GObject *object)
1193 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1195 if (data_sheet->dispose_has_run)
1198 data_sheet->dispose_has_run = TRUE;
1200 psppire_data_sheet_unset_data_store (data_sheet);
1202 g_object_unref (data_sheet->builder);
1204 if (data_sheet->uim)
1205 g_object_unref (data_sheet->uim);
1207 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1211 psppire_data_sheet_map (GtkWidget *widget)
1215 GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
1217 clip = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
1218 g_signal_connect (clip, "owner-change", G_CALLBACK (on_owner_change),
1220 on_owner_change (clip, NULL, widget);
1224 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1226 GObjectClass *gobject_class;
1227 GtkWidgetClass *widget_class;
1229 gobject_class = G_OBJECT_CLASS (class);
1230 gobject_class->set_property = psppire_data_sheet_set_property;
1231 gobject_class->get_property = psppire_data_sheet_get_property;
1232 gobject_class->dispose = psppire_data_sheet_dispose;
1234 widget_class = GTK_WIDGET_CLASS (class);
1235 widget_class->map = psppire_data_sheet_map;
1237 g_signal_new ("var-double-clicked",
1238 G_OBJECT_CLASS_TYPE (gobject_class),
1241 g_signal_accumulator_true_handled, NULL,
1242 psppire_marshal_BOOLEAN__INT,
1243 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1245 g_object_class_install_property (
1246 gobject_class, PROP_DATA_STORE,
1247 g_param_spec_object ("data-store",
1249 "The data store for the data sheet to display.",
1250 PSPPIRE_TYPE_DATA_STORE,
1251 G_PARAM_WRITABLE | G_PARAM_READABLE));
1253 g_object_class_install_property (
1254 gobject_class, PROP_VALUE_LABELS,
1255 g_param_spec_boolean ("value-labels",
1257 "Whether or not the data sheet should display labels instead of values",
1259 G_PARAM_WRITABLE | G_PARAM_READABLE));
1261 g_object_class_install_property (
1262 gobject_class, PROP_CASE_NUMBERS,
1263 g_param_spec_boolean ("case-numbers",
1265 "Whether or not the data sheet should display case numbers",
1267 G_PARAM_WRITABLE | G_PARAM_READABLE));
1269 g_object_class_install_property (
1272 g_param_spec_long ("current-case",
1274 "Zero based number of the selected case",
1277 G_PARAM_WRITABLE | G_PARAM_READABLE));
1279 g_object_class_install_property (
1281 PROP_MAY_CREATE_VARS,
1282 g_param_spec_boolean ("may-create-vars",
1283 "May create variables",
1284 "Whether the user may create more variables",
1286 G_PARAM_READWRITE));
1288 g_object_class_install_property (
1290 PROP_MAY_DELETE_VARS,
1291 g_param_spec_boolean ("may-delete-vars",
1292 "May delete variables",
1293 "Whether the user may delete variables",
1295 G_PARAM_READWRITE));
1297 g_object_class_install_property (
1300 g_param_spec_object ("ui-manager",
1302 "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.",
1303 GTK_TYPE_UI_MANAGER,
1308 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1310 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1313 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1314 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1318 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1320 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1324 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1325 gpointer user_data UNUSED)
1327 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1329 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1331 PsppSheetSelection *selection;
1333 selection = pspp_sheet_view_get_selection (sheet_view);
1334 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1338 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1339 &path, NULL, NULL, NULL))
1341 pspp_sheet_selection_unselect_all (selection);
1342 pspp_sheet_selection_select_path (selection, path);
1343 pspp_sheet_selection_select_all_columns (selection);
1344 gtk_tree_path_free (path);
1348 do_popup_menu (widget, event->button, event->time);
1357 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1359 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1360 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1361 const struct range_set_node *node;
1362 struct range_set *selected;
1364 selected = pspp_sheet_selection_get_range_set (selection);
1365 for (node = range_set_last (selected); node != NULL;
1366 node = range_set_prev (selected, node))
1368 unsigned long int start = range_set_node_get_start (node);
1369 unsigned long int count = range_set_node_get_width (node);
1371 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1373 range_set_destroy (selected);
1377 on_selection_changed (PsppSheetSelection *selection,
1378 gpointer user_data UNUSED)
1380 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1381 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1382 gint n_selected_rows;
1383 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1388 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1390 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1391 gtk_action_set_sensitive (action, n_selected_rows > 0);
1393 switch (n_selected_rows)
1396 may_delete_cases = FALSE;
1400 /* The row used for inserting new cases cannot be deleted. */
1401 path = gtk_tree_path_new_from_indices (
1402 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1403 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1405 gtk_tree_path_free (path);
1409 may_delete_cases = TRUE;
1412 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1413 gtk_action_set_sensitive (action, may_delete_cases);
1415 may_delete_vars = may_insert_vars = FALSE;
1416 list = pspp_sheet_selection_get_selected_columns (selection);
1417 for (iter = list; iter != NULL; iter = iter->next)
1419 PsppSheetViewColumn *column = iter->data;
1420 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1424 may_delete_vars = may_insert_vars = TRUE;
1427 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1428 may_insert_vars = TRUE;
1432 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1433 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1435 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1436 gtk_action_set_sensitive (action, may_insert_vars);
1438 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1439 gtk_action_set_sensitive (action, may_delete_vars);
1441 action = get_action_assert (data_sheet->builder, "sort-up");
1442 gtk_action_set_sensitive (action, may_delete_vars);
1444 action = get_action_assert (data_sheet->builder, "sort-down");
1445 gtk_action_set_sensitive (action, may_delete_vars);
1447 psppire_data_sheet_update_clip_actions (data_sheet);
1451 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1452 struct range_set **rowsp,
1453 struct range_set **colsp)
1455 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1456 PsppireDataStore *data_store = data_sheet->data_store;
1457 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1458 unsigned long n_cases;
1459 struct range_set *rows, *cols;
1462 if (data_store == NULL)
1464 n_cases = psppire_data_store_get_case_count (data_store);
1466 rows = pspp_sheet_selection_get_range_set (selection);
1467 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1468 if (range_set_is_empty (rows))
1470 range_set_destroy (rows);
1474 cols = range_set_create ();
1475 list = pspp_sheet_selection_get_selected_columns (selection);
1476 for (iter = list; iter != NULL; iter = iter->next)
1478 PsppSheetViewColumn *column = iter->data;
1479 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1482 range_set_set1 (cols, var_get_dict_index (var), 1);
1485 if (range_set_is_empty (cols))
1487 range_set_destroy (rows);
1488 range_set_destroy (cols);
1498 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1500 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1501 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1502 PsppireDataStore *data_store = data_sheet->data_store;
1503 struct range_set *selected;
1506 selected = pspp_sheet_selection_get_range_set (selection);
1507 row = range_set_scan (selected, 0);
1508 range_set_destroy (selected);
1510 if (row <= psppire_data_store_get_case_count (data_store))
1511 psppire_data_store_insert_new_case (data_store, row);
1515 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1517 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1518 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1519 PsppireDict *dict = data_sheet->data_store->dict;
1520 PsppSheetViewColumn *column;
1521 struct variable *var;
1526 list = pspp_sheet_selection_get_selected_columns (selection);
1529 column = list->data;
1532 var = g_object_get_data (G_OBJECT (column), "variable");
1533 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1534 if (psppire_dict_generate_name (dict, name, sizeof name))
1535 psppire_dict_insert_variable (dict, index, name);
1539 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1541 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1542 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1543 PsppireDict *dict = data_sheet->data_store->dict;
1546 list = pspp_sheet_selection_get_selected_columns (selection);
1549 list = g_list_reverse (list);
1550 for (iter = list; iter; iter = iter->next)
1552 PsppSheetViewColumn *column = iter->data;
1553 struct variable *var;
1555 var = g_object_get_data (G_OBJECT (column), "variable");
1557 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1569 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1571 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1572 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1573 PsppireDataWindow *pdw;
1578 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1579 g_return_if_fail (pdw != NULL);
1581 list = pspp_sheet_selection_get_selected_columns (selection);
1583 syntax = g_string_new ("SORT CASES BY");
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");
1593 g_string_append_printf (syntax, " %s", var_get_name (var));
1599 if (order == SORT_DESCEND)
1600 g_string_append (syntax, " (DOWN)");
1601 g_string_append_c (syntax, '.');
1602 execute_const_syntax_string (pdw, syntax->str);
1604 g_string_free (syntax, TRUE);
1608 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1610 do_sort (data_sheet, SORT_ASCEND);
1614 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1616 do_sort (data_sheet, SORT_DESCEND);
1620 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1622 goto_case_dialog (data_sheet);
1626 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1628 PsppireDataWindow *pdw;
1630 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1631 g_return_if_fail (pdw != NULL);
1637 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1639 psppire_data_sheet_set_clip (data_sheet, FALSE);
1643 on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet)
1645 psppire_data_sheet_set_clip (data_sheet, TRUE);
1649 on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet)
1651 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
1652 GtkClipboard *clipboard =
1653 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1655 gtk_clipboard_request_contents (clipboard,
1656 gdk_atom_intern ("UTF8_STRING", TRUE),
1657 psppire_data_sheet_clip_received_cb,
1662 psppire_data_sheet_init (PsppireDataSheet *obj)
1664 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1667 obj->show_value_labels = FALSE;
1668 obj->show_case_numbers = TRUE;
1669 obj->may_create_vars = TRUE;
1670 obj->may_delete_vars = TRUE;
1672 obj->scroll_to_bottom_signal = 0;
1673 obj->scroll_to_right_signal = 0;
1674 obj->new_variable_column = NULL;
1675 obj->container = NULL;
1678 obj->dispose_has_run = FALSE;
1680 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1682 g_signal_connect (obj, "notify::model",
1683 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1685 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1686 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1687 PSPP_SHEET_SELECTION_RECTANGLE);
1689 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1690 g_signal_connect (obj, "query-tooltip",
1691 G_CALLBACK (on_query_tooltip), NULL);
1692 g_signal_connect (obj, "button-press-event",
1693 G_CALLBACK (on_button_pressed), NULL);
1694 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1696 obj->builder = builder_new ("data-sheet.ui");
1698 action = get_action_assert (obj->builder, "edit_clear-cases");
1699 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1701 gtk_action_set_sensitive (action, FALSE);
1702 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1703 "changed", G_CALLBACK (on_selection_changed), NULL);
1705 action = get_action_assert (obj->builder, "edit_insert-case");
1706 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1709 action = get_action_assert (obj->builder, "edit_insert-variable");
1710 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1713 action = get_action_assert (obj->builder, "edit_goto-case");
1714 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1717 action = get_action_assert (obj->builder, "edit_copy");
1718 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1720 action = get_action_assert (obj->builder, "edit_cut");
1721 g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj);
1723 action = get_action_assert (obj->builder, "edit_paste");
1724 g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj);
1726 action = get_action_assert (obj->builder, "edit_clear-variables");
1727 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1730 action = get_action_assert (obj->builder, "edit_find");
1731 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1733 action = get_action_assert (obj->builder, "sort-up");
1734 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1736 action = get_action_assert (obj->builder, "sort-down");
1737 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1742 psppire_data_sheet_new (void)
1744 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1748 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1750 return data_sheet->data_store;
1754 refresh_model (PsppireDataSheet *data_sheet)
1756 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1758 if (data_sheet->data_store != NULL)
1760 PsppireEmptyListStore *model;
1764 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1765 model = psppire_empty_list_store_new (n_rows);
1766 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1767 GTK_TREE_MODEL (model));
1768 g_object_unref (model);
1770 action = get_action_assert (data_sheet->builder, "edit_copy");
1771 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1777 on_case_inserted (PsppireDataStore *data_store, gint row,
1778 PsppireDataSheet *data_sheet)
1780 PsppireEmptyListStore *empty_list_store;
1781 GtkTreeModel *tree_model;
1784 g_return_if_fail (data_store == data_sheet->data_store);
1786 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1787 if (row == n_rows - 1)
1790 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1791 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1792 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1793 psppire_empty_list_store_row_inserted (empty_list_store, row);
1797 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1798 PsppireDataSheet *data_sheet)
1801 g_return_if_fail (data_store == data_sheet->data_store);
1805 /* This is a bit of a cop-out. We could do better, if it ever turns out
1806 that this performs too poorly. */
1807 refresh_model (data_sheet);
1811 PsppireEmptyListStore *empty_list_store;
1812 GtkTreeModel *tree_model;
1813 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1815 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1816 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1817 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1818 psppire_empty_list_store_row_deleted (empty_list_store, first);
1823 on_case_change (PsppireDataStore *data_store, gint row,
1824 PsppireDataSheet *data_sheet)
1826 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1828 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1829 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1833 on_backend_changed (PsppireDataStore *data_store,
1834 PsppireDataSheet *data_sheet)
1836 g_return_if_fail (data_store == data_sheet->data_store);
1837 refresh_model (data_sheet);
1841 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1842 PsppireDataSheet *data_sheet)
1844 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1845 PsppSheetViewColumn *column;
1846 struct variable *var;
1850 g_return_if_fail (data_sheet->data_store != NULL);
1851 g_return_if_fail (dict == data_sheet->data_store->dict);
1853 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1858 var = psppire_dict_get_variable (data_store->dict, dict_index);
1859 g_return_if_fail (var != NULL);
1861 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1862 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1863 if (display_width != var_get_display_width (var))
1865 gint base_width, incr_width;
1867 display_width = var_get_display_width (var);
1868 calc_width_conversion (data_sheet, &base_width, &incr_width);
1869 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1870 base_width, incr_width);
1871 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1876 on_variable_changed (PsppireDict *dict, int dict_index,
1877 PsppireDataSheet *data_sheet)
1879 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1880 PsppSheetViewColumn *column;
1881 GtkCellRenderer *cell;
1882 struct variable *var;
1886 g_return_if_fail (data_sheet->data_store != NULL);
1887 g_return_if_fail (dict == data_sheet->data_store->dict);
1889 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1894 var = psppire_dict_get_variable (data_store->dict, dict_index);
1895 g_return_if_fail (var != NULL);
1897 name = escape_underscores (var_get_name (var));
1898 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1899 pspp_sheet_view_column_set_title (column, name);
1902 cells = pspp_sheet_view_column_get_cell_renderers (column);
1903 g_return_if_fail (cells);
1905 g_list_free (cells);
1907 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1909 /* Stop editing before we delete and replace the cell renderers.
1910 Otherwise if this column is currently being edited, an eventual call
1911 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1912 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1914 It's possible that this is a bug in PsppSheetView, and it's possible
1915 that PsppSheetView inherits that from GtkTreeView, but I haven't
1916 investigated yet. */
1917 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1919 add_data_column_cell_renderer (data_sheet, column);
1924 on_variable_inserted (PsppireDict *dict, int var_index,
1925 PsppireDataSheet *data_sheet)
1927 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1928 gint base_width, incr_width;
1929 PsppSheetViewColumn *column;
1931 calc_width_conversion (data_sheet, &base_width, &incr_width);
1932 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1933 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1937 on_variable_deleted (PsppireDict *dict,
1938 const struct variable *var, int case_idx, int width,
1939 PsppireDataSheet *data_sheet)
1941 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1942 GList *columns, *iter;
1944 columns = pspp_sheet_view_get_columns (sheet_view);
1945 for (iter = columns; iter != NULL; iter = iter->next)
1947 PsppSheetViewColumn *column = iter->data;
1948 const struct variable *column_var;
1950 column_var = g_object_get_data (G_OBJECT (column), "variable");
1951 if (column_var == var)
1952 pspp_sheet_view_remove_column (sheet_view, column);
1954 g_list_free (columns);
1958 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1960 PsppireDataStore *store = data_sheet->data_store;
1965 data_sheet->data_store = NULL;
1967 g_signal_handlers_disconnect_by_func (
1968 store, G_CALLBACK (on_backend_changed), data_sheet);
1969 g_signal_handlers_disconnect_by_func (
1970 store, G_CALLBACK (on_case_inserted), data_sheet);
1971 g_signal_handlers_disconnect_by_func (
1972 store, G_CALLBACK (on_cases_deleted), data_sheet);
1973 g_signal_handlers_disconnect_by_func (
1974 store, G_CALLBACK (on_case_change), data_sheet);
1976 g_signal_handlers_disconnect_by_func (
1977 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
1978 g_signal_handlers_disconnect_by_func (
1979 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
1980 g_signal_handlers_disconnect_by_func (
1981 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
1982 g_signal_handlers_disconnect_by_func (
1983 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
1985 g_object_unref (store);
1989 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
1990 PsppireDataStore *data_store)
1992 psppire_data_sheet_unset_data_store (data_sheet);
1994 data_sheet->data_store = data_store;
1995 if (data_store != NULL)
1997 g_object_ref (data_store);
1998 g_signal_connect (data_store, "backend-changed",
1999 G_CALLBACK (on_backend_changed), data_sheet);
2000 g_signal_connect (data_store, "case-inserted",
2001 G_CALLBACK (on_case_inserted), data_sheet);
2002 g_signal_connect (data_store, "cases-deleted",
2003 G_CALLBACK (on_cases_deleted), data_sheet);
2004 g_signal_connect (data_store, "case-changed",
2005 G_CALLBACK (on_case_change), data_sheet);
2007 /* XXX it's unclean to hook into the dict this way--what if the dict
2008 changes? As of this writing, though, nothing ever changes the
2009 data_store's dict. */
2010 g_signal_connect (data_store->dict, "variable-changed",
2011 G_CALLBACK (on_variable_changed),
2013 g_signal_connect (data_store->dict, "variable-display-width-changed",
2014 G_CALLBACK (on_variable_display_width_changed),
2016 g_signal_connect (data_store->dict, "variable-inserted",
2017 G_CALLBACK (on_variable_inserted), data_sheet);
2018 g_signal_connect (data_store->dict, "variable-deleted",
2019 G_CALLBACK (on_variable_deleted), data_sheet);
2021 refresh_model (data_sheet);
2024 /* Clipboard stuff */
2026 /* A casereader and dictionary holding the data currently in the clip */
2027 static struct casereader *clip_datasheet = NULL;
2028 static struct dictionary *clip_dict = NULL;
2031 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2033 /* Set the clip from the currently selected range in DATA_SHEET. If CUT is
2034 true, clears the original data from DATA_SHEET, otherwise leaves the
2035 original data in-place. */
2037 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2040 struct casewriter *writer ;
2041 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2042 struct case_map *map = NULL;
2043 struct range_set *rows, *cols;
2044 const struct range_set_node *node;
2046 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2050 /* Destroy any existing clip */
2051 if ( clip_datasheet )
2053 casereader_destroy (clip_datasheet);
2054 clip_datasheet = NULL;
2059 dict_destroy (clip_dict);
2063 /* Construct clip dictionary. */
2064 clip_dict = dict_create (dict_get_encoding (ds->dict->dict));
2065 RANGE_SET_FOR_EACH (node, cols)
2069 for (dict_index = node->start; dict_index < node->end; dict_index++)
2071 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2072 dict_clone_var_assert (clip_dict, var);
2076 /* Construct clip data. */
2077 map = case_map_by_name (ds->dict->dict, clip_dict);
2078 writer = autopaging_writer_create (dict_get_proto (clip_dict));
2079 RANGE_SET_FOR_EACH (node, rows)
2081 unsigned long int row;
2083 for (row = node->start; row < node->end; row++)
2085 struct ccase *old = psppire_data_store_get_case (ds, row);
2087 casewriter_write (writer, case_map_execute (map, old));
2089 casewriter_force_error (writer);
2092 case_map_destroy (map);
2094 /* Clear data that we copied out, if we're doing a "cut". */
2095 if (cut && !casewriter_error (writer))
2097 RANGE_SET_FOR_EACH (node, rows)
2099 unsigned long int row;
2101 for (row = node->start; row < node->end; row++)
2103 const struct range_set_node *node2;
2105 RANGE_SET_FOR_EACH (node2, cols)
2109 for (dict_index = node2->start; dict_index < node2->end;
2112 struct variable *var;
2114 var = dict_get_var (ds->dict->dict, dict_index);
2115 psppire_data_store_set_string (ds, "", row,
2123 range_set_destroy (rows);
2124 range_set_destroy (cols);
2126 clip_datasheet = casewriter_make_reader (writer);
2128 psppire_data_sheet_update_clipboard (data_sheet);
2138 /* Perform data_out for case CC, variable V, appending to STRING */
2140 data_out_g_string (GString *string, const struct variable *v,
2141 const struct ccase *cc)
2143 const struct fmt_spec *fs = var_get_print_format (v);
2144 const union value *val = case_data (cc, v);
2146 char *s = data_out (val, var_get_encoding (v), fs);
2148 g_string_append (string, s);
2159 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2160 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2161 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2163 string = g_string_sized_new (10 * val_cnt * case_cnt);
2165 for (r = 0 ; r < case_cnt ; ++r )
2170 cc = casereader_peek (clip_datasheet, r);
2173 g_warning ("Clipboard seems to have inexplicably shrunk");
2177 for (c = 0 ; c < var_cnt ; ++c)
2179 const struct variable *v = dict_get_var (clip_dict, c);
2180 data_out_g_string (string, v, cc);
2181 if ( c < val_cnt - 1 )
2182 g_string_append (string, "\t");
2186 g_string_append (string, "\n");
2201 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2202 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2203 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2205 /* Guestimate the size needed */
2206 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2208 g_string_append (string,
2209 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2211 g_string_append (string, "<table>\n");
2212 for (r = 0 ; r < case_cnt ; ++r )
2215 struct ccase *cc = casereader_peek (clip_datasheet, r);
2218 g_warning ("Clipboard seems to have inexplicably shrunk");
2221 g_string_append (string, "<tr>\n");
2223 for (c = 0 ; c < var_cnt ; ++c)
2225 const struct variable *v = dict_get_var (clip_dict, c);
2226 g_string_append (string, "<td>");
2227 data_out_g_string (string, v, cc);
2228 g_string_append (string, "</td>\n");
2231 g_string_append (string, "</tr>\n");
2235 g_string_append (string, "</table>\n");
2243 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2244 GtkSelectionData *selection_data,
2248 GString *string = NULL;
2252 case SELECT_FMT_TEXT:
2253 string = clip_to_text ();
2255 case SELECT_FMT_HTML:
2256 string = clip_to_html ();
2259 g_assert_not_reached ();
2262 gtk_selection_data_set (selection_data, selection_data->target,
2264 (const guchar *) string->str, string->len);
2266 g_string_free (string, TRUE);
2270 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2273 dict_destroy (clip_dict);
2276 casereader_destroy (clip_datasheet);
2277 clip_datasheet = NULL;
2281 static const GtkTargetEntry targets[] = {
2282 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2283 { "STRING", 0, SELECT_FMT_TEXT },
2284 { "TEXT", 0, SELECT_FMT_TEXT },
2285 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2286 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2287 { "text/plain", 0, SELECT_FMT_TEXT },
2288 { "text/html", 0, SELECT_FMT_HTML }
2294 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2296 GtkClipboard *clipboard =
2297 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2298 GDK_SELECTION_CLIPBOARD);
2300 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2301 G_N_ELEMENTS (targets),
2302 psppire_data_sheet_clipboard_get_cb,
2303 psppire_data_sheet_clipboard_clear_cb,
2305 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2309 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2311 struct range_set *rows, *cols;
2315 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2318 range_set_destroy (rows);
2319 range_set_destroy (cols);
2322 action = get_action_assert (data_sheet->builder, "edit_copy");
2323 gtk_action_set_sensitive (action, enable);
2325 action = get_action_assert (data_sheet->builder, "edit_cut");
2326 gtk_action_set_sensitive (action, enable);
2329 /* A callback for when the clipboard contents have been received. */
2331 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2332 GtkSelectionData *sd,
2335 PsppireDataSheet *data_sheet = data;
2336 PsppireDataStore *store = data_sheet->data_store;
2337 struct range_set *rows, *cols;
2339 gint next_row, next_column;
2343 if ( sd->length < 0 )
2346 if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
2349 c = (char *) sd->data;
2351 /* Get the starting selected position in the data sheet. (Possibly we should
2352 only paste into the selected range if it's larger than one cell?) */
2353 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2355 next_row = range_set_first (rows)->start;
2356 first_column = next_column = range_set_first (cols)->start;
2357 range_set_destroy (rows);
2358 range_set_destroy (cols);
2360 g_return_if_fail (next_row >= 0);
2361 g_return_if_fail (next_column >= 0);
2363 while (count < sd->length)
2365 gint row = next_row;
2366 gint column = next_column;
2367 struct variable *var;
2370 while (*c != '\t' && *c != '\n' && count < sd->length)
2378 next_column = column + 1;
2380 else if ( *c == '\n')
2383 next_column = first_column;
2388 var = psppire_dict_get_variable (store->dict, column);
2390 psppire_data_store_set_string (store, s, row, var, FALSE);
2395 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2397 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2398 gboolean compatible_target = FALSE;
2402 for (i = 0; i < G_N_ELEMENTS (targets); i++)
2404 GdkAtom atom = gdk_atom_intern (targets[i].target, TRUE);
2405 if (gtk_clipboard_wait_is_target_available (clip, atom))
2407 compatible_target = TRUE;
2412 action = get_action_assert (data_sheet->builder, "edit_paste");
2413 gtk_action_set_sensitive (action, compatible_target);