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 *);
56 static void on_selection_changed (PsppSheetSelection *, gpointer);
58 G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, PSPP_TYPE_SHEET_VIEW);
61 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
63 size_t *row, PsppSheetViewColumn **columnp)
65 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
69 PsppSheetViewColumn *tree_column;
70 GtkTreeModel *tree_model;
73 /* Check that WIDGET is really visible on the screen before we
74 do anything else. This is a bug fix for a sticky situation:
75 when text_data_import_assistant() returns, it frees the data
76 necessary to compose the tool tip message, but there may be
77 a tool tip under preparation at that point (even if there is
78 no visible tool tip) that will call back into us a little
79 bit later. Perhaps the correct solution to this problem is
80 to make the data related to the tool tips part of a GObject
81 that only gets destroyed when all references are released,
82 but this solution appears to be effective too. */
83 if (!gtk_widget_get_mapped (widget))
86 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
88 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
89 &path, &tree_column, NULL, NULL))
92 *columnp = tree_column;
94 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
97 tree_model = pspp_sheet_view_get_model (tree_view);
98 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
99 gtk_tree_path_free (path);
103 *row = GPOINTER_TO_INT (iter.user_data);
108 on_query_tooltip (GtkWidget *widget, gint wx, gint wy,
109 gboolean keyboard_mode UNUSED,
110 GtkTooltip *tooltip, gpointer data UNUSED)
112 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
113 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
114 PsppSheetViewColumn *column;
115 struct variable *var;
121 g_return_val_if_fail (data_store != NULL, FALSE);
123 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
126 var = g_object_get_data (G_OBJECT (column), "variable");
129 if (g_object_get_data (G_OBJECT (column), "new-var-column") == NULL)
132 gtk_tooltip_set_text (tooltip,
133 _("Enter a number to add a new variable."));
136 else if (row >= datasheet_get_n_rows (data_store->datasheet))
138 gtk_tooltip_set_text (tooltip, _("Enter a number to add a new case."));
142 width = var_get_width (var);
144 value_init (&v, width);
145 datasheet_get_value (data_store->datasheet, row, var_get_case_index (var),
148 label = var_lookup_value_label (var, &v);
151 if (data_sheet->show_value_labels)
153 char *s = value_to_text (v, var);
154 gtk_tooltip_set_text (tooltip, s);
158 gtk_tooltip_set_text (tooltip, label);
160 value_destroy (&v, width);
162 return label != NULL;
166 render_row_number_cell (PsppSheetViewColumn *tree_column,
167 GtkCellRenderer *cell,
172 PsppireDataStore *store = store_;
173 GValue gvalue = { 0, };
176 row = GPOINTER_TO_INT (iter->user_data);
178 g_value_init (&gvalue, G_TYPE_INT);
179 g_value_set_int (&gvalue, row + 1);
180 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
181 g_value_unset (&gvalue);
183 if (row < datasheet_get_n_rows (store->datasheet))
184 g_object_set (cell, "editable", TRUE, NULL);
186 g_object_set (cell, "editable", FALSE, NULL);
189 "slash", psppire_data_store_filtered (store, row),
194 on_row_number_clicked (PsppireCellRendererButton *button,
196 PsppSheetView *sheet_view)
198 PsppSheetSelection *selection;
201 path = gtk_tree_path_new_from_string (path_string);
203 selection = pspp_sheet_view_get_selection (sheet_view);
204 pspp_sheet_selection_unselect_all (selection);
205 pspp_sheet_selection_select_path (selection, path);
206 pspp_sheet_selection_select_all_columns (selection);
208 gtk_tree_path_free (path);
212 make_row_number_column (PsppireDataSheet *data_sheet,
213 PsppireDataStore *ds)
215 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
216 PsppSheetViewColumn *column;
217 GtkCellRenderer *renderer;
219 renderer = psppire_cell_renderer_button_new ();
220 g_object_set (renderer, "xalign", 1.0, NULL);
221 g_signal_connect (renderer, "clicked", G_CALLBACK (on_row_number_clicked),
224 column = pspp_sheet_view_column_new_with_attributes (_("Case"),
226 g_object_set (column, "selectable", FALSE, "row-head", TRUE, NULL);
227 pspp_sheet_view_column_set_cell_data_func (
228 column, renderer, render_row_number_cell, ds, NULL);
229 pspp_sheet_view_column_set_fixed_width (column, 50);
230 pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers);
231 pspp_sheet_view_append_column (sheet_view, column);
235 render_data_cell (PsppSheetViewColumn *tree_column,
236 GtkCellRenderer *cell,
239 gpointer data_sheet_)
241 PsppireDataSheet *data_sheet = data_sheet_;
242 PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet);
243 struct variable *var;
249 row = GPOINTER_TO_INT (iter->user_data);
250 var = g_object_get_data (G_OBJECT (tree_column), "variable");
252 string = psppire_data_store_get_string (store, row, var,
253 data_sheet->show_value_labels);
256 GValue gvalue = { 0 };
258 g_value_init (&gvalue, G_TYPE_STRING);
259 g_value_take_string (&gvalue, string);
260 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
261 g_value_unset (&gvalue);
264 g_object_set (G_OBJECT (cell), "text", "", NULL);
266 switch (var_get_alignment (var))
268 case ALIGN_LEFT: xalign = 0.0; break;
269 case ALIGN_RIGHT: xalign = 1.0; break;
270 case ALIGN_CENTRE: xalign = 0.5; break;
271 default: xalign = 0.0; break;
280 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
284 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
285 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
286 NULL, NULL, NULL, &width, NULL);
291 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
298 ds_put_byte_multiple (&s, '0', char_cnt);
299 ds_put_byte (&s, ' ');
300 width = get_string_width (treeview, renderer, ds_cstr (&s));
307 on_data_column_editing_started (GtkCellRenderer *cell,
308 GtkCellEditable *editable,
312 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
313 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
314 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
315 struct variable *var;
317 g_return_if_fail (column);
318 g_return_if_fail (data_sheet);
319 g_return_if_fail (data_store);
322 g_object_ref (editable);
323 g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable",
324 editable, g_object_unref);
326 var = g_object_get_data (G_OBJECT (column), "variable");
327 g_return_if_fail (var);
329 if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable))
331 const struct val_labs *labels = var_get_value_labels (var);
332 const struct val_lab *vl;
333 GtkListStore *list_store;
335 list_store = gtk_list_store_new (1, G_TYPE_STRING);
336 for (vl = val_labs_first (labels); vl != NULL;
337 vl = val_labs_next (labels, vl))
341 gtk_list_store_append (list_store, &iter);
342 gtk_list_store_set (list_store, &iter,
343 0, val_lab_get_label (vl),
347 gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
348 GTK_TREE_MODEL (list_store));
349 g_object_unref (list_store);
354 scroll_to_bottom (GtkWidget *widget,
355 GtkRequisition *requisition,
356 gpointer unused UNUSED)
358 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
359 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
360 GtkAdjustment *vadjust;
362 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
363 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
365 if (data_sheet->scroll_to_bottom_signal)
367 g_signal_handler_disconnect (data_sheet,
368 data_sheet->scroll_to_bottom_signal);
369 data_sheet->scroll_to_bottom_signal = 0;
374 on_data_column_edited (GtkCellRendererText *cell,
379 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
380 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
381 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
382 GtkEditable *editable;
383 struct variable *var;
389 path = gtk_tree_path_new_from_string (path_string);
390 row = gtk_tree_path_get_indices (path)[0];
391 gtk_tree_path_free (path);
393 var = g_object_get_data (G_OBJECT (column), "variable");
395 new_row = row == psppire_data_store_get_case_count (data_store);
396 if (new_row && new_text[0] == '\0')
399 editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
400 g_return_if_fail (editable != NULL);
401 is_val_lab = (GTK_IS_COMBO_BOX (editable)
402 && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
403 g_object_unref (editable);
405 psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
407 if (new_row && !data_sheet->scroll_to_bottom_signal)
409 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
410 data_sheet->scroll_to_bottom_signal =
411 g_signal_connect (data_sheet, "size-request",
412 G_CALLBACK (scroll_to_bottom), NULL);
416 /* We could be more specific about what to redraw, if it seems
417 important for performance. */
418 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
423 scroll_to_right (GtkWidget *widget,
424 PsppireDataSheet *data_sheet)
426 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
427 PsppSheetViewColumn *column, *prev;
428 GList *columns, *iter;
432 columns = pspp_sheet_view_get_columns (sheet_view);
433 for (iter = columns; iter; iter = iter->next)
435 PsppSheetViewColumn *c = iter->data;
436 if (g_object_get_data (G_OBJECT (c), "new-var-column"))
443 g_list_free (columns);
448 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
454 pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
457 pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
458 gtk_tree_path_free (path);
462 if (data_sheet->scroll_to_right_signal)
464 g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
465 data_sheet->scroll_to_right_signal = 0;
470 on_new_variable_column_edited (GtkCellRendererText *cell,
475 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
476 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
477 PsppireDict *dict = data_store->dict;
478 struct variable *var;
483 if (new_text[0] == '\0')
485 /* User didn't enter anything so don't create a variable. */
489 path = gtk_tree_path_new_from_string (path_string);
490 row = gtk_tree_path_get_indices (path)[0];
491 gtk_tree_path_free (path);
493 if (!psppire_dict_generate_name (dict, name, sizeof name))
496 var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
498 g_return_if_fail (var != NULL);
500 psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
502 if (!data_sheet->scroll_to_right_signal)
504 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
505 data_sheet->scroll_to_right_signal =
506 g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
507 G_CALLBACK (scroll_to_right), data_sheet);
511 /* We could be more specific about what to redraw, if it seems
512 important for performance. */
513 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
518 calc_width_conversion (PsppireDataSheet *data_sheet,
519 gint *base_width, gint *incr_width)
521 GtkCellRenderer *cell;
524 cell = gtk_cell_renderer_text_new ();
525 w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
526 w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
527 *incr_width = MAX (1, (w10 - w1) / 9);
528 *base_width = MAX (0, w10 - *incr_width * 10);
529 g_object_ref_sink (cell);
530 g_object_unref (cell);
534 display_width_from_pixel_width (PsppireDataSheet *data_sheet,
537 gint base_width, incr_width;
539 calc_width_conversion (data_sheet, &base_width, &incr_width);
540 return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
544 display_width_to_pixel_width (PsppireDataSheet *data_sheet,
549 return base_width + incr_width * display_width;
553 on_data_column_resized (GObject *gobject,
557 PsppireDataSheet *data_sheet = user_data;
558 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
559 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
560 struct variable *var;
564 if (data_store == NULL)
567 pixel_width = pspp_sheet_view_column_get_width (column);
568 if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
570 /* Short-circuit the expensive display_width_from_pixel_width()
571 calculation, to make loading .sav files with 2000 columns visibly
576 var = g_object_get_data (G_OBJECT (column), "variable");
577 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
578 var_set_display_width (var, display_width);
582 do_data_column_popup_menu (PsppSheetViewColumn *column,
583 guint button, guint32 time)
585 GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
586 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
589 menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
590 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
594 on_data_column_popup_menu (PsppSheetViewColumn *column,
595 gpointer user_data UNUSED)
597 do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
601 on_column_button_press_event (PsppSheetViewColumn *column,
602 GdkEventButton *event,
603 gpointer user_data UNUSED)
605 PsppSheetSelection *selection;
606 PsppSheetView *sheet_view;
608 sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
610 g_return_val_if_fail (sheet_view != NULL, FALSE);
612 selection = pspp_sheet_view_get_selection (sheet_view);
613 g_return_val_if_fail (selection != NULL, FALSE);
615 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
617 do_data_column_popup_menu (column, event->button, event->time);
620 else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
622 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
623 struct variable *var;
625 var = g_object_get_data (G_OBJECT (column), "variable");
630 g_signal_emit_by_name (data_sheet, "var-double-clicked",
631 var_get_dict_index (var), &handled);
640 on_data_column_query_tooltip (PsppSheetViewColumn *column,
642 gpointer user_data UNUSED)
644 struct variable *var;
647 var = g_object_get_data (G_OBJECT (column), "variable");
648 g_return_val_if_fail (var != NULL, FALSE);
650 text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
651 gtk_tooltip_set_text (tooltip, text);
657 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
658 PsppSheetViewColumn *column)
660 GtkCellRenderer *cell;
661 struct variable *var;
663 var = g_object_get_data (G_OBJECT (column), "variable");
664 g_return_if_fail (var != NULL);
666 if (var_has_value_labels (var))
668 cell = gtk_cell_renderer_combo_new ();
669 g_object_set (G_OBJECT (cell),
675 cell = gtk_cell_renderer_text_new ();
677 g_signal_connect (cell, "editing-started",
678 G_CALLBACK (on_data_column_editing_started), NULL);
679 g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
681 g_object_set_data (G_OBJECT (cell), "column", column);
682 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
684 pspp_sheet_view_column_clear (column);
685 pspp_sheet_view_column_pack_start (column, cell, TRUE);
687 pspp_sheet_view_column_set_cell_data_func (
688 column, cell, render_data_cell, data_sheet, NULL);
691 static PsppSheetViewColumn *
692 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
693 gint base_width, gint incr_width)
695 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
696 struct variable *var;
697 PsppSheetViewColumn *column;
701 var = psppire_dict_get_variable (data_store->dict, dict_idx);
703 column = pspp_sheet_view_column_new ();
705 name = escape_underscores (var_get_name (var));
706 pspp_sheet_view_column_set_title (column, name);
709 g_object_set_data (G_OBJECT (column), "variable", var);
711 width = display_width_to_pixel_width (data_sheet,
712 var_get_display_width (var),
713 base_width, incr_width);
714 pspp_sheet_view_column_set_min_width (column, 10);
715 pspp_sheet_view_column_set_fixed_width (column, width);
716 pspp_sheet_view_column_set_resizable (column, TRUE);
718 pspp_sheet_view_column_set_clickable (column, TRUE);
719 g_signal_connect (column, "notify::width",
720 G_CALLBACK (on_data_column_resized), data_sheet);
722 g_signal_connect (column, "button-press-event",
723 G_CALLBACK (on_column_button_press_event),
725 g_signal_connect (column, "query-tooltip",
726 G_CALLBACK (on_data_column_query_tooltip), NULL);
727 g_signal_connect (column, "popup-menu",
728 G_CALLBACK (on_data_column_popup_menu), data_sheet);
730 add_data_column_cell_renderer (data_sheet, column);
736 make_new_variable_column (PsppireDataSheet *data_sheet,
737 gint base_width, gint incr_width)
739 PsppSheetViewColumn *column;
740 GtkCellRenderer *cell;
743 cell = gtk_cell_renderer_text_new ();
744 g_object_set (cell, "editable", TRUE, NULL);
746 g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
749 column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
750 g_object_set_data (G_OBJECT (column), "new-var-column", column);
752 width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
753 pspp_sheet_view_column_set_min_width (column, 10);
754 pspp_sheet_view_column_set_fixed_width (column, width);
756 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
757 g_signal_connect (column, "button-press-event",
758 G_CALLBACK (on_column_button_press_event),
760 g_signal_connect (column, "popup-menu",
761 G_CALLBACK (on_data_column_popup_menu), data_sheet);
763 pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
765 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
766 data_sheet->new_variable_column = column;
770 psppire_data_sheet_model_changed (GObject *gobject,
774 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
775 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
776 PsppireDataStore *data_store;
778 /* Remove old columns. */
781 PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
785 pspp_sheet_view_remove_column (sheet_view, column);
787 data_sheet->new_variable_column = NULL;
789 if (pspp_sheet_view_get_model (sheet_view) == NULL)
791 /* Don't create any columns at all if there's no model. Otherwise we'll
792 create some columns as part of the "dispose" callback for the sheet
793 view, which sets the model to NULL. That causes warnings to be
794 logged and is obviously undesirable in any case. */
798 /* Add new columns. */
799 data_store = psppire_data_sheet_get_data_store (data_sheet);
800 if (data_store != NULL)
802 gint base_width, incr_width;
805 calc_width_conversion (data_sheet, &base_width, &incr_width);
807 make_row_number_column (data_sheet, data_store);
808 for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
810 PsppSheetViewColumn *column;
812 column = make_data_column (data_sheet, i, base_width, incr_width);
813 pspp_sheet_view_append_column (sheet_view, column);
815 make_new_variable_column (data_sheet, base_width, incr_width);
826 PROP_MAY_CREATE_VARS,
827 PROP_MAY_DELETE_VARS,
832 psppire_data_sheet_set_property (GObject *object,
837 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
841 case PROP_DATA_STORE:
842 psppire_data_sheet_set_data_store (
843 obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
846 case PROP_VALUE_LABELS:
847 psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
850 case PROP_CASE_NUMBERS:
851 psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
854 case PROP_CURRENT_CASE:
855 psppire_data_sheet_goto_case (obj, g_value_get_long (value));
858 case PROP_MAY_CREATE_VARS:
859 psppire_data_sheet_set_may_create_vars (obj,
860 g_value_get_boolean (value));
863 case PROP_MAY_DELETE_VARS:
864 psppire_data_sheet_set_may_delete_vars (obj,
865 g_value_get_boolean (value));
868 case PROP_UI_MANAGER:
870 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
876 psppire_data_sheet_get_property (GObject *object,
881 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
885 case PROP_DATA_STORE:
886 g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
889 case PROP_VALUE_LABELS:
890 g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
893 case PROP_CASE_NUMBERS:
894 g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
897 case PROP_CURRENT_CASE:
898 g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
901 case PROP_MAY_CREATE_VARS:
902 g_value_set_boolean (value, obj->may_create_vars);
905 case PROP_MAY_DELETE_VARS:
906 g_value_set_boolean (value, obj->may_delete_vars);
909 case PROP_UI_MANAGER:
910 g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
914 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
920 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
922 return ds->show_value_labels;
926 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
927 gboolean show_value_labels)
929 show_value_labels = !!show_value_labels;
930 if (show_value_labels != ds->show_value_labels)
932 ds->show_value_labels = show_value_labels;
933 g_object_notify (G_OBJECT (ds), "value-labels");
934 gtk_widget_queue_draw (GTK_WIDGET (ds));
936 /* Make the cell being edited refresh too. */
937 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (ds), TRUE);
942 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
944 return ds->show_case_numbers;
948 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
949 gboolean show_case_numbers)
951 show_case_numbers = !!show_case_numbers;
952 if (show_case_numbers != ds->show_case_numbers)
954 PsppSheetViewColumn *column;
956 ds->show_case_numbers = show_case_numbers;
957 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
959 pspp_sheet_view_column_set_visible (column, show_case_numbers);
961 g_object_notify (G_OBJECT (ds), "case-numbers");
962 gtk_widget_queue_draw (GTK_WIDGET (ds));
967 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
969 return data_sheet->may_create_vars;
973 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
974 gboolean may_create_vars)
976 if (data_sheet->may_create_vars != may_create_vars)
978 data_sheet->may_create_vars = may_create_vars;
979 if (data_sheet->new_variable_column)
980 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
983 on_selection_changed (pspp_sheet_view_get_selection (
984 PSPP_SHEET_VIEW (data_sheet)), NULL);
989 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
991 return data_sheet->may_delete_vars;
995 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
996 gboolean may_delete_vars)
998 if (data_sheet->may_delete_vars != may_delete_vars)
1000 data_sheet->may_delete_vars = may_delete_vars;
1001 on_selection_changed (pspp_sheet_view_get_selection (
1002 PSPP_SHEET_VIEW (data_sheet)), NULL);
1006 static PsppSheetViewColumn *
1007 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1010 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1011 PsppireDataStore *data_store;
1012 PsppSheetViewColumn *column;
1013 struct variable *var;
1016 data_store = psppire_data_sheet_get_data_store (data_sheet);
1017 g_return_val_if_fail (data_store != NULL, NULL);
1018 g_return_val_if_fail (data_store->dict != NULL, NULL);
1020 var = psppire_dict_get_variable (data_store->dict, dict_index);
1021 g_return_val_if_fail (var != NULL, NULL);
1024 list = pspp_sheet_view_get_columns (sheet_view);
1025 for (iter = list; iter != NULL; iter = iter->next)
1027 PsppSheetViewColumn *c = iter->data;
1030 v = g_object_get_data (G_OBJECT (c), "variable");
1043 psppire_data_sheet_show_variable (PsppireDataSheet *data_sheet,
1046 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1047 PsppSheetViewColumn *column;
1049 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1052 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column,
1057 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1059 PsppSheetSelection *selection;
1060 struct variable *var;
1061 GList *selected_columns;
1064 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1065 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1068 for (iter = selected_columns; iter != NULL; iter = iter->next)
1070 PsppSheetViewColumn *column = iter->data;
1071 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1084 g_list_free (selected_columns);
1090 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1092 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1093 PsppireDataStore *store = data_sheet->data_store;
1094 PsppSheetSelection *selection;
1097 g_return_if_fail (case_index >= 0);
1098 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1100 path = gtk_tree_path_new_from_indices (case_index, -1);
1102 /* Select the case. */
1103 selection = pspp_sheet_view_get_selection (sheet_view);
1104 pspp_sheet_selection_unselect_all (selection);
1105 pspp_sheet_selection_select_path (selection, path);
1106 pspp_sheet_selection_select_all_columns (selection);
1108 /* Scroll so that the case is visible. */
1109 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1111 gtk_tree_path_free (path);
1114 /* Returns the 0-based index of a selected case, if there is at least one, and
1117 If more than one case is selected, returns the one with the smallest index,
1118 that is, the index of the case closest to the beginning of the file. The
1119 row that can be used to insert a new case is not considered a case. */
1121 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1123 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1124 PsppireDataStore *store = data_sheet->data_store;
1125 const struct range_set_node *node;
1126 PsppSheetSelection *selection;
1127 struct range_set *rows;
1130 selection = pspp_sheet_view_get_selection (sheet_view);
1131 rows = pspp_sheet_selection_get_range_set (selection);
1132 node = range_set_first (rows);
1133 row = (node && node->start < psppire_data_store_get_case_count (store)
1136 range_set_destroy (rows);
1141 /* Returns the 0-based index of a selected case, if exactly one case is
1142 selected, and -1 otherwise. Returns -1 if the row that can be used to
1143 insert a new case is selected. */
1145 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1147 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1148 PsppireDataStore *store = data_sheet->data_store;
1149 const struct range_set_node *node;
1150 PsppSheetSelection *selection;
1151 struct range_set *rows;
1154 selection = pspp_sheet_view_get_selection (sheet_view);
1155 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1158 rows = pspp_sheet_selection_get_range_set (selection);
1159 node = range_set_first (rows);
1160 row = (node && node->start < psppire_data_store_get_case_count (store)
1163 range_set_destroy (rows);
1169 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1171 if (data_sheet->uim == NULL)
1174 GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1176 GTK_TYPE_UI_MANAGER));
1177 g_object_ref (data_sheet->uim);
1180 return data_sheet->uim;
1184 psppire_data_sheet_dispose (GObject *object)
1186 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1188 if (data_sheet->dispose_has_run)
1191 data_sheet->dispose_has_run = TRUE;
1193 psppire_data_sheet_unset_data_store (data_sheet);
1195 g_object_unref (data_sheet->builder);
1197 if (data_sheet->uim)
1198 g_object_unref (data_sheet->uim);
1200 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1204 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1206 GObjectClass *gobject_class;
1208 gobject_class = G_OBJECT_CLASS (class);
1209 gobject_class->set_property = psppire_data_sheet_set_property;
1210 gobject_class->get_property = psppire_data_sheet_get_property;
1212 gobject_class->dispose = psppire_data_sheet_dispose;
1214 g_signal_new ("var-double-clicked",
1215 G_OBJECT_CLASS_TYPE (gobject_class),
1218 g_signal_accumulator_true_handled, NULL,
1219 psppire_marshal_BOOLEAN__INT,
1220 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1222 g_object_class_install_property (
1223 gobject_class, PROP_DATA_STORE,
1224 g_param_spec_object ("data-store",
1226 "The data store for the data sheet to display.",
1227 PSPPIRE_TYPE_DATA_STORE,
1228 G_PARAM_WRITABLE | G_PARAM_READABLE));
1230 g_object_class_install_property (
1231 gobject_class, PROP_VALUE_LABELS,
1232 g_param_spec_boolean ("value-labels",
1234 "Whether or not the data sheet should display labels instead of values",
1236 G_PARAM_WRITABLE | G_PARAM_READABLE));
1238 g_object_class_install_property (
1239 gobject_class, PROP_CASE_NUMBERS,
1240 g_param_spec_boolean ("case-numbers",
1242 "Whether or not the data sheet should display case numbers",
1244 G_PARAM_WRITABLE | G_PARAM_READABLE));
1246 g_object_class_install_property (
1249 g_param_spec_long ("current-case",
1251 "Zero based number of the selected case",
1254 G_PARAM_WRITABLE | G_PARAM_READABLE));
1256 g_object_class_install_property (
1258 PROP_MAY_CREATE_VARS,
1259 g_param_spec_boolean ("may-create-vars",
1260 "May create variables",
1261 "Whether the user may create more variables",
1263 G_PARAM_READWRITE));
1265 g_object_class_install_property (
1267 PROP_MAY_DELETE_VARS,
1268 g_param_spec_boolean ("may-delete-vars",
1269 "May delete variables",
1270 "Whether the user may delete variables",
1272 G_PARAM_READWRITE));
1274 g_object_class_install_property (
1277 g_param_spec_object ("ui-manager",
1279 "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.",
1280 GTK_TYPE_UI_MANAGER,
1285 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1287 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1290 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1291 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1295 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1297 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1301 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1302 gpointer user_data UNUSED)
1304 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1306 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1308 PsppSheetSelection *selection;
1310 selection = pspp_sheet_view_get_selection (sheet_view);
1311 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1315 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1316 &path, NULL, NULL, NULL))
1318 pspp_sheet_selection_unselect_all (selection);
1319 pspp_sheet_selection_select_path (selection, path);
1320 pspp_sheet_selection_select_all_columns (selection);
1321 gtk_tree_path_free (path);
1325 do_popup_menu (widget, event->button, event->time);
1334 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1336 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1337 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1338 const struct range_set_node *node;
1339 struct range_set *selected;
1341 selected = pspp_sheet_selection_get_range_set (selection);
1342 for (node = range_set_last (selected); node != NULL;
1343 node = range_set_prev (selected, node))
1345 unsigned long int start = range_set_node_get_start (node);
1346 unsigned long int count = range_set_node_get_width (node);
1348 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1350 range_set_destroy (selected);
1354 on_selection_changed (PsppSheetSelection *selection,
1355 gpointer user_data UNUSED)
1357 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1358 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1359 gint n_selected_rows;
1360 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1365 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1367 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1368 gtk_action_set_sensitive (action, n_selected_rows > 0);
1370 switch (n_selected_rows)
1373 may_delete_cases = FALSE;
1377 /* The row used for inserting new cases cannot be deleted. */
1378 path = gtk_tree_path_new_from_indices (
1379 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1380 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1382 gtk_tree_path_free (path);
1386 may_delete_cases = TRUE;
1389 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1390 gtk_action_set_sensitive (action, may_delete_cases);
1392 may_delete_vars = may_insert_vars = FALSE;
1393 list = pspp_sheet_selection_get_selected_columns (selection);
1394 for (iter = list; iter != NULL; iter = iter->next)
1396 PsppSheetViewColumn *column = iter->data;
1397 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1401 may_delete_vars = may_insert_vars = TRUE;
1404 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1405 may_insert_vars = TRUE;
1409 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1410 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1412 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1413 gtk_action_set_sensitive (action, may_insert_vars);
1415 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1416 gtk_action_set_sensitive (action, may_delete_vars);
1418 action = get_action_assert (data_sheet->builder, "sort-up");
1419 gtk_action_set_sensitive (action, may_delete_vars);
1421 action = get_action_assert (data_sheet->builder, "sort-down");
1422 gtk_action_set_sensitive (action, may_delete_vars);
1424 psppire_data_sheet_update_clip_actions (data_sheet);
1428 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1429 struct range_set **rowsp,
1430 struct range_set **colsp)
1432 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1433 PsppireDataStore *data_store = data_sheet->data_store;
1434 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1435 unsigned long n_cases;
1436 struct range_set *rows, *cols;
1439 if (data_store == NULL)
1441 n_cases = psppire_data_store_get_case_count (data_store);
1443 rows = pspp_sheet_selection_get_range_set (selection);
1444 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1445 if (range_set_is_empty (rows))
1447 range_set_destroy (rows);
1451 cols = range_set_create ();
1452 list = pspp_sheet_selection_get_selected_columns (selection);
1453 for (iter = list; iter != NULL; iter = iter->next)
1455 PsppSheetViewColumn *column = iter->data;
1456 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1459 range_set_set1 (cols, var_get_dict_index (var), 1);
1462 if (range_set_is_empty (cols))
1464 range_set_destroy (rows);
1465 range_set_destroy (cols);
1475 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1477 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1478 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1479 PsppireDataStore *data_store = data_sheet->data_store;
1480 struct range_set *selected;
1483 selected = pspp_sheet_selection_get_range_set (selection);
1484 row = range_set_scan (selected, 0);
1485 range_set_destroy (selected);
1487 if (row <= psppire_data_store_get_case_count (data_store))
1488 psppire_data_store_insert_new_case (data_store, row);
1492 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1494 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1495 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1496 PsppireDict *dict = data_sheet->data_store->dict;
1497 PsppSheetViewColumn *column;
1498 struct variable *var;
1503 list = pspp_sheet_selection_get_selected_columns (selection);
1506 column = list->data;
1509 var = g_object_get_data (G_OBJECT (column), "variable");
1510 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1511 if (psppire_dict_generate_name (dict, name, sizeof name))
1512 psppire_dict_insert_variable (dict, index, name);
1516 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1518 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1519 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1520 PsppireDict *dict = data_sheet->data_store->dict;
1523 list = pspp_sheet_selection_get_selected_columns (selection);
1526 list = g_list_reverse (list);
1527 for (iter = list; iter; iter = iter->next)
1529 PsppSheetViewColumn *column = iter->data;
1530 struct variable *var;
1532 var = g_object_get_data (G_OBJECT (column), "variable");
1534 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1546 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1548 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1549 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1550 PsppireDataWindow *pdw;
1555 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1556 g_return_if_fail (pdw != NULL);
1558 list = pspp_sheet_selection_get_selected_columns (selection);
1560 syntax = g_string_new ("SORT CASES BY");
1562 for (iter = list; iter; iter = iter->next)
1564 PsppSheetViewColumn *column = iter->data;
1565 struct variable *var;
1567 var = g_object_get_data (G_OBJECT (column), "variable");
1570 g_string_append_printf (syntax, " %s", var_get_name (var));
1576 if (order == SORT_DESCEND)
1577 g_string_append (syntax, " (DOWN)");
1578 g_string_append_c (syntax, '.');
1579 execute_const_syntax_string (pdw, syntax->str);
1581 g_string_free (syntax, TRUE);
1585 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1587 do_sort (data_sheet, SORT_ASCEND);
1591 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1593 do_sort (data_sheet, SORT_DESCEND);
1597 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1599 goto_case_dialog (data_sheet);
1603 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1605 PsppireDataWindow *pdw;
1607 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1608 g_return_if_fail (pdw != NULL);
1614 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1616 psppire_data_sheet_set_clip (data_sheet);
1620 psppire_data_sheet_init (PsppireDataSheet *obj)
1622 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1625 obj->show_value_labels = FALSE;
1626 obj->show_case_numbers = TRUE;
1627 obj->may_create_vars = TRUE;
1628 obj->may_delete_vars = TRUE;
1630 obj->scroll_to_bottom_signal = 0;
1631 obj->scroll_to_right_signal = 0;
1632 obj->new_variable_column = NULL;
1633 obj->container = NULL;
1636 obj->dispose_has_run = FALSE;
1638 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1640 g_signal_connect (obj, "notify::model",
1641 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1643 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1644 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1645 PSPP_SHEET_SELECTION_RECTANGLE);
1647 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1648 g_signal_connect (obj, "query-tooltip",
1649 G_CALLBACK (on_query_tooltip), NULL);
1650 g_signal_connect (obj, "button-press-event",
1651 G_CALLBACK (on_button_pressed), NULL);
1652 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1654 obj->builder = builder_new ("data-sheet.ui");
1656 action = get_action_assert (obj->builder, "edit_clear-cases");
1657 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1659 gtk_action_set_sensitive (action, FALSE);
1660 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1661 "changed", G_CALLBACK (on_selection_changed), NULL);
1663 action = get_action_assert (obj->builder, "edit_insert-case");
1664 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1667 action = get_action_assert (obj->builder, "edit_insert-variable");
1668 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1671 action = get_action_assert (obj->builder, "edit_goto-case");
1672 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1675 action = get_action_assert (obj->builder, "edit_copy");
1676 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1678 action = get_action_assert (obj->builder, "edit_clear-variables");
1679 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1682 action = get_action_assert (obj->builder, "edit_find");
1683 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1685 action = get_action_assert (obj->builder, "sort-up");
1686 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1688 action = get_action_assert (obj->builder, "sort-down");
1689 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1694 psppire_data_sheet_new (void)
1696 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1700 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1702 return data_sheet->data_store;
1706 refresh_model (PsppireDataSheet *data_sheet)
1708 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1710 if (data_sheet->data_store != NULL)
1712 PsppireEmptyListStore *model;
1716 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1717 model = psppire_empty_list_store_new (n_rows);
1718 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1719 GTK_TREE_MODEL (model));
1720 g_object_unref (model);
1722 action = get_action_assert (data_sheet->builder, "edit_copy");
1723 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1729 on_case_inserted (PsppireDataStore *data_store, gint row,
1730 PsppireDataSheet *data_sheet)
1732 PsppireEmptyListStore *empty_list_store;
1733 GtkTreeModel *tree_model;
1736 g_return_if_fail (data_store == data_sheet->data_store);
1738 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1739 if (row == n_rows - 1)
1742 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1743 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1744 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1745 psppire_empty_list_store_row_inserted (empty_list_store, row);
1749 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1750 PsppireDataSheet *data_sheet)
1753 g_return_if_fail (data_store == data_sheet->data_store);
1757 /* This is a bit of a cop-out. We could do better, if it ever turns out
1758 that this performs too poorly. */
1759 refresh_model (data_sheet);
1763 PsppireEmptyListStore *empty_list_store;
1764 GtkTreeModel *tree_model;
1765 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1767 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1768 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1769 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1770 psppire_empty_list_store_row_deleted (empty_list_store, first);
1775 on_case_change (PsppireDataStore *data_store, gint row,
1776 PsppireDataSheet *data_sheet)
1778 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1780 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1781 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1785 on_backend_changed (PsppireDataStore *data_store,
1786 PsppireDataSheet *data_sheet)
1788 g_return_if_fail (data_store == data_sheet->data_store);
1789 refresh_model (data_sheet);
1793 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1794 PsppireDataSheet *data_sheet)
1796 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1797 PsppSheetViewColumn *column;
1798 struct variable *var;
1802 g_return_if_fail (data_sheet->data_store != NULL);
1803 g_return_if_fail (dict == data_sheet->data_store->dict);
1805 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1810 var = psppire_dict_get_variable (data_store->dict, dict_index);
1811 g_return_if_fail (var != NULL);
1813 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1814 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1815 if (display_width != var_get_display_width (var))
1817 gint base_width, incr_width;
1819 display_width = var_get_display_width (var);
1820 calc_width_conversion (data_sheet, &base_width, &incr_width);
1821 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1822 base_width, incr_width);
1823 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1828 on_variable_changed (PsppireDict *dict, int dict_index,
1829 PsppireDataSheet *data_sheet)
1831 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1832 PsppSheetViewColumn *column;
1833 GtkCellRenderer *cell;
1834 struct variable *var;
1838 g_return_if_fail (data_sheet->data_store != NULL);
1839 g_return_if_fail (dict == data_sheet->data_store->dict);
1841 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1846 var = psppire_dict_get_variable (data_store->dict, dict_index);
1847 g_return_if_fail (var != NULL);
1849 name = escape_underscores (var_get_name (var));
1850 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1851 pspp_sheet_view_column_set_title (column, name);
1854 cells = pspp_sheet_view_column_get_cell_renderers (column);
1855 g_return_if_fail (cells);
1857 g_list_free (cells);
1859 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1861 /* Stop editing before we delete and replace the cell renderers.
1862 Otherwise if this column is currently being edited, an eventual call
1863 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1864 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1866 It's possible that this is a bug in PsppSheetView, and it's possible
1867 that PsppSheetView inherits that from GtkTreeView, but I haven't
1868 investigated yet. */
1869 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1871 add_data_column_cell_renderer (data_sheet, column);
1876 on_variable_inserted (PsppireDict *dict, int var_index,
1877 PsppireDataSheet *data_sheet)
1879 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1880 gint base_width, incr_width;
1881 PsppSheetViewColumn *column;
1883 calc_width_conversion (data_sheet, &base_width, &incr_width);
1884 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1885 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1889 on_variable_deleted (PsppireDict *dict,
1890 const struct variable *var, int case_idx, int width,
1891 PsppireDataSheet *data_sheet)
1893 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1894 GList *columns, *iter;
1896 columns = pspp_sheet_view_get_columns (sheet_view);
1897 for (iter = columns; iter != NULL; iter = iter->next)
1899 PsppSheetViewColumn *column = iter->data;
1900 const struct variable *column_var;
1902 column_var = g_object_get_data (G_OBJECT (column), "variable");
1903 if (column_var == var)
1904 pspp_sheet_view_remove_column (sheet_view, column);
1906 g_list_free (columns);
1910 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1912 PsppireDataStore *store = data_sheet->data_store;
1917 data_sheet->data_store = NULL;
1919 g_signal_handlers_disconnect_by_func (
1920 store, G_CALLBACK (on_backend_changed), data_sheet);
1921 g_signal_handlers_disconnect_by_func (
1922 store, G_CALLBACK (on_case_inserted), data_sheet);
1923 g_signal_handlers_disconnect_by_func (
1924 store, G_CALLBACK (on_cases_deleted), data_sheet);
1925 g_signal_handlers_disconnect_by_func (
1926 store, G_CALLBACK (on_case_change), data_sheet);
1928 g_signal_handlers_disconnect_by_func (
1929 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
1930 g_signal_handlers_disconnect_by_func (
1931 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
1932 g_signal_handlers_disconnect_by_func (
1933 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
1934 g_signal_handlers_disconnect_by_func (
1935 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
1937 g_object_unref (store);
1941 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
1942 PsppireDataStore *data_store)
1944 psppire_data_sheet_unset_data_store (data_sheet);
1946 data_sheet->data_store = data_store;
1947 if (data_store != NULL)
1949 g_object_ref (data_store);
1950 g_signal_connect (data_store, "backend-changed",
1951 G_CALLBACK (on_backend_changed), data_sheet);
1952 g_signal_connect (data_store, "case-inserted",
1953 G_CALLBACK (on_case_inserted), data_sheet);
1954 g_signal_connect (data_store, "cases-deleted",
1955 G_CALLBACK (on_cases_deleted), data_sheet);
1956 g_signal_connect (data_store, "case-changed",
1957 G_CALLBACK (on_case_change), data_sheet);
1959 /* XXX it's unclean to hook into the dict this way--what if the dict
1960 changes? As of this writing, though, nothing ever changes the
1961 data_store's dict. */
1962 g_signal_connect (data_store->dict, "variable-changed",
1963 G_CALLBACK (on_variable_changed),
1965 g_signal_connect (data_store->dict, "variable-display-width-changed",
1966 G_CALLBACK (on_variable_display_width_changed),
1968 g_signal_connect (data_store->dict, "variable-inserted",
1969 G_CALLBACK (on_variable_inserted), data_sheet);
1970 g_signal_connect (data_store->dict, "variable-deleted",
1971 G_CALLBACK (on_variable_deleted), data_sheet);
1973 refresh_model (data_sheet);
1976 /* Clipboard stuff */
1978 /* A casereader and dictionary holding the data currently in the clip */
1979 static struct casereader *clip_datasheet = NULL;
1980 static struct dictionary *clip_dict = NULL;
1983 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
1985 /* Set the clip according to the currently
1986 selected range in the data sheet */
1988 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet)
1990 struct casewriter *writer ;
1991 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
1992 struct case_map *map = NULL;
1993 struct range_set *rows, *cols;
1994 const struct range_set_node *node;
1996 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2000 /* Destroy any existing clip */
2001 if ( clip_datasheet )
2003 casereader_destroy (clip_datasheet);
2004 clip_datasheet = NULL;
2009 dict_destroy (clip_dict);
2013 /* Construct clip dictionary. */
2014 clip_dict = dict_create (dict_get_encoding (ds->dict->dict));
2015 RANGE_SET_FOR_EACH (node, cols)
2019 for (dict_index = node->start; dict_index < node->end; dict_index++)
2021 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2022 dict_clone_var_assert (clip_dict, var);
2026 /* Construct clip data. */
2027 map = case_map_by_name (ds->dict->dict, clip_dict);
2028 writer = autopaging_writer_create (dict_get_proto (clip_dict));
2029 RANGE_SET_FOR_EACH (node, rows)
2031 unsigned long int row;
2033 for (row = node->start; row < node->end; row++)
2035 struct ccase *old = psppire_data_store_get_case (ds, row);
2037 casewriter_write (writer, case_map_execute (map, old));
2039 casewriter_force_error (writer);
2042 case_map_destroy (map);
2044 range_set_destroy (rows);
2045 range_set_destroy (cols);
2047 clip_datasheet = casewriter_make_reader (writer);
2049 psppire_data_sheet_update_clipboard (data_sheet);
2059 /* Perform data_out for case CC, variable V, appending to STRING */
2061 data_out_g_string (GString *string, const struct variable *v,
2062 const struct ccase *cc)
2064 const struct fmt_spec *fs = var_get_print_format (v);
2065 const union value *val = case_data (cc, v);
2067 char *s = data_out (val, var_get_encoding (v), fs);
2069 g_string_append (string, s);
2080 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2081 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2082 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2084 string = g_string_sized_new (10 * val_cnt * case_cnt);
2086 for (r = 0 ; r < case_cnt ; ++r )
2091 cc = casereader_peek (clip_datasheet, r);
2094 g_warning ("Clipboard seems to have inexplicably shrunk");
2098 for (c = 0 ; c < var_cnt ; ++c)
2100 const struct variable *v = dict_get_var (clip_dict, c);
2101 data_out_g_string (string, v, cc);
2102 if ( c < val_cnt - 1 )
2103 g_string_append (string, "\t");
2107 g_string_append (string, "\n");
2122 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2123 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2124 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2126 /* Guestimate the size needed */
2127 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2129 g_string_append (string,
2130 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2132 g_string_append (string, "<table>\n");
2133 for (r = 0 ; r < case_cnt ; ++r )
2136 struct ccase *cc = casereader_peek (clip_datasheet, r);
2139 g_warning ("Clipboard seems to have inexplicably shrunk");
2142 g_string_append (string, "<tr>\n");
2144 for (c = 0 ; c < var_cnt ; ++c)
2146 const struct variable *v = dict_get_var (clip_dict, c);
2147 g_string_append (string, "<td>");
2148 data_out_g_string (string, v, cc);
2149 g_string_append (string, "</td>\n");
2152 g_string_append (string, "</tr>\n");
2156 g_string_append (string, "</table>\n");
2164 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2165 GtkSelectionData *selection_data,
2169 GString *string = NULL;
2173 case SELECT_FMT_TEXT:
2174 string = clip_to_text ();
2176 case SELECT_FMT_HTML:
2177 string = clip_to_html ();
2180 g_assert_not_reached ();
2183 gtk_selection_data_set (selection_data, selection_data->target,
2185 (const guchar *) string->str, string->len);
2187 g_string_free (string, TRUE);
2191 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2194 dict_destroy (clip_dict);
2197 casereader_destroy (clip_datasheet);
2198 clip_datasheet = NULL;
2202 static const GtkTargetEntry targets[] = {
2203 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2204 { "STRING", 0, SELECT_FMT_TEXT },
2205 { "TEXT", 0, SELECT_FMT_TEXT },
2206 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2207 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2208 { "text/plain", 0, SELECT_FMT_TEXT },
2209 { "text/html", 0, SELECT_FMT_HTML }
2215 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2217 GtkClipboard *clipboard =
2218 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2219 GDK_SELECTION_CLIPBOARD);
2221 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2222 G_N_ELEMENTS (targets),
2223 psppire_data_sheet_clipboard_get_cb,
2224 psppire_data_sheet_clipboard_clear_cb,
2226 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2230 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2232 struct range_set *rows, *cols;
2236 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2239 range_set_destroy (rows);
2240 range_set_destroy (cols);
2243 action = get_action_assert (data_sheet->builder, "edit_copy");
2244 gtk_action_set_sensitive (action, enable);
2246 action = get_action_assert (data_sheet->builder, "edit_cut");
2247 gtk_action_set_sensitive (action, enable);