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_destroy (GtkObject *);
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 return GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1173 GTK_TYPE_UI_MANAGER));
1177 psppire_data_sheet_destroy (GtkObject *object)
1179 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1181 psppire_data_sheet_unset_data_store (data_sheet);
1182 if (data_sheet->builder)
1184 g_object_unref (data_sheet->builder);
1185 data_sheet->builder = NULL;
1188 GTK_OBJECT_CLASS (psppire_data_sheet_parent_class)->destroy (object);
1192 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1194 GObjectClass *gobject_class;
1195 GtkObjectClass *gtk_object_class;
1197 gobject_class = G_OBJECT_CLASS (class);
1198 gobject_class->set_property = psppire_data_sheet_set_property;
1199 gobject_class->get_property = psppire_data_sheet_get_property;
1201 gtk_object_class = GTK_OBJECT_CLASS (class);
1202 gtk_object_class->destroy = psppire_data_sheet_destroy;
1204 g_signal_new ("var-double-clicked",
1205 G_OBJECT_CLASS_TYPE (gobject_class),
1208 g_signal_accumulator_true_handled, NULL,
1209 psppire_marshal_BOOLEAN__INT,
1210 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1212 g_object_class_install_property (
1213 gobject_class, PROP_DATA_STORE,
1214 g_param_spec_object ("data-store",
1216 "The data store for the data sheet to display.",
1217 PSPPIRE_TYPE_DATA_STORE,
1218 G_PARAM_WRITABLE | G_PARAM_READABLE));
1220 g_object_class_install_property (
1221 gobject_class, PROP_VALUE_LABELS,
1222 g_param_spec_boolean ("value-labels",
1224 "Whether or not the data sheet should display labels instead of values",
1226 G_PARAM_WRITABLE | G_PARAM_READABLE));
1228 g_object_class_install_property (
1229 gobject_class, PROP_CASE_NUMBERS,
1230 g_param_spec_boolean ("case-numbers",
1232 "Whether or not the data sheet should display case numbers",
1234 G_PARAM_WRITABLE | G_PARAM_READABLE));
1236 g_object_class_install_property (
1239 g_param_spec_long ("current-case",
1241 "Zero based number of the selected case",
1244 G_PARAM_WRITABLE | G_PARAM_READABLE));
1246 g_object_class_install_property (
1248 PROP_MAY_CREATE_VARS,
1249 g_param_spec_boolean ("may-create-vars",
1250 "May create variables",
1251 "Whether the user may create more variables",
1253 G_PARAM_READWRITE));
1255 g_object_class_install_property (
1257 PROP_MAY_DELETE_VARS,
1258 g_param_spec_boolean ("may-delete-vars",
1259 "May delete variables",
1260 "Whether the user may delete variables",
1262 G_PARAM_READWRITE));
1264 g_object_class_install_property (
1267 g_param_spec_object ("ui-manager",
1269 "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.",
1270 GTK_TYPE_UI_MANAGER,
1275 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1277 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1280 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1281 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1285 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1287 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1291 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1292 gpointer user_data UNUSED)
1294 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1296 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1298 PsppSheetSelection *selection;
1300 selection = pspp_sheet_view_get_selection (sheet_view);
1301 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1305 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1306 &path, NULL, NULL, NULL))
1308 pspp_sheet_selection_unselect_all (selection);
1309 pspp_sheet_selection_select_path (selection, path);
1310 pspp_sheet_selection_select_all_columns (selection);
1311 gtk_tree_path_free (path);
1315 do_popup_menu (widget, event->button, event->time);
1324 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1326 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1327 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1328 const struct range_set_node *node;
1329 struct range_set *selected;
1331 selected = pspp_sheet_selection_get_range_set (selection);
1332 for (node = range_set_last (selected); node != NULL;
1333 node = range_set_prev (selected, node))
1335 unsigned long int start = range_set_node_get_start (node);
1336 unsigned long int count = range_set_node_get_width (node);
1338 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1340 range_set_destroy (selected);
1344 on_selection_changed (PsppSheetSelection *selection,
1345 gpointer user_data UNUSED)
1347 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1348 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1349 gint n_selected_rows;
1350 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1355 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1357 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1358 gtk_action_set_sensitive (action, n_selected_rows > 0);
1360 switch (n_selected_rows)
1363 may_delete_cases = FALSE;
1367 /* The row used for inserting new cases cannot be deleted. */
1368 path = gtk_tree_path_new_from_indices (
1369 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1370 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1372 gtk_tree_path_free (path);
1376 may_delete_cases = TRUE;
1379 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1380 gtk_action_set_sensitive (action, may_delete_cases);
1382 may_delete_vars = may_insert_vars = FALSE;
1383 list = pspp_sheet_selection_get_selected_columns (selection);
1384 for (iter = list; iter != NULL; iter = iter->next)
1386 PsppSheetViewColumn *column = iter->data;
1387 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1391 may_delete_vars = may_insert_vars = TRUE;
1394 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1395 may_insert_vars = TRUE;
1399 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1400 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1402 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1403 gtk_action_set_sensitive (action, may_insert_vars);
1405 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1406 gtk_action_set_sensitive (action, may_delete_vars);
1408 action = get_action_assert (data_sheet->builder, "sort-up");
1409 gtk_action_set_sensitive (action, may_delete_vars);
1411 action = get_action_assert (data_sheet->builder, "sort-down");
1412 gtk_action_set_sensitive (action, may_delete_vars);
1414 psppire_data_sheet_update_clip_actions (data_sheet);
1418 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1419 struct range_set **rowsp,
1420 struct range_set **colsp)
1422 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1423 PsppireDataStore *data_store = data_sheet->data_store;
1424 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1425 unsigned long n_cases;
1426 struct range_set *rows, *cols;
1429 if (data_store == NULL)
1431 n_cases = psppire_data_store_get_case_count (data_store);
1433 rows = pspp_sheet_selection_get_range_set (selection);
1434 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1435 if (range_set_is_empty (rows))
1437 range_set_destroy (rows);
1441 cols = range_set_create ();
1442 list = pspp_sheet_selection_get_selected_columns (selection);
1443 for (iter = list; iter != NULL; iter = iter->next)
1445 PsppSheetViewColumn *column = iter->data;
1446 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1449 range_set_set1 (cols, var_get_dict_index (var), 1);
1452 if (range_set_is_empty (cols))
1454 range_set_destroy (rows);
1455 range_set_destroy (cols);
1465 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1467 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1468 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1469 PsppireDataStore *data_store = data_sheet->data_store;
1470 struct range_set *selected;
1473 selected = pspp_sheet_selection_get_range_set (selection);
1474 row = range_set_scan (selected, 0);
1475 range_set_destroy (selected);
1477 if (row <= psppire_data_store_get_case_count (data_store))
1478 psppire_data_store_insert_new_case (data_store, row);
1482 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1484 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1485 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1486 PsppireDict *dict = data_sheet->data_store->dict;
1487 PsppSheetViewColumn *column;
1488 struct variable *var;
1493 list = pspp_sheet_selection_get_selected_columns (selection);
1496 column = list->data;
1499 var = g_object_get_data (G_OBJECT (column), "variable");
1500 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1501 if (psppire_dict_generate_name (dict, name, sizeof name))
1502 psppire_dict_insert_variable (dict, index, name);
1506 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1508 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1509 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1510 PsppireDict *dict = data_sheet->data_store->dict;
1513 list = pspp_sheet_selection_get_selected_columns (selection);
1516 list = g_list_reverse (list);
1517 for (iter = list; iter; iter = iter->next)
1519 PsppSheetViewColumn *column = iter->data;
1520 struct variable *var;
1522 var = g_object_get_data (G_OBJECT (column), "variable");
1524 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1536 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1538 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1539 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1540 PsppireDataWindow *pdw;
1545 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1546 g_return_if_fail (pdw != NULL);
1548 list = pspp_sheet_selection_get_selected_columns (selection);
1550 syntax = g_string_new ("SORT CASES BY");
1552 for (iter = list; iter; iter = iter->next)
1554 PsppSheetViewColumn *column = iter->data;
1555 struct variable *var;
1557 var = g_object_get_data (G_OBJECT (column), "variable");
1560 g_string_append_printf (syntax, " %s", var_get_name (var));
1566 if (order == SORT_DESCEND)
1567 g_string_append (syntax, " (DOWN)");
1568 g_string_append_c (syntax, '.');
1569 execute_const_syntax_string (pdw, syntax->str);
1571 g_string_free (syntax, TRUE);
1575 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1577 do_sort (data_sheet, SORT_ASCEND);
1581 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1583 do_sort (data_sheet, SORT_DESCEND);
1587 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1589 goto_case_dialog (data_sheet);
1593 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1595 PsppireDataWindow *pdw;
1597 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1598 g_return_if_fail (pdw != NULL);
1604 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1606 psppire_data_sheet_set_clip (data_sheet);
1610 psppire_data_sheet_init (PsppireDataSheet *obj)
1612 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1615 obj->show_value_labels = FALSE;
1616 obj->show_case_numbers = TRUE;
1617 obj->may_create_vars = TRUE;
1618 obj->may_delete_vars = TRUE;
1620 obj->scroll_to_bottom_signal = 0;
1621 obj->scroll_to_right_signal = 0;
1622 obj->new_variable_column = NULL;
1623 obj->container = NULL;
1625 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1627 g_signal_connect (obj, "notify::model",
1628 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1630 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1631 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1632 PSPP_SHEET_SELECTION_RECTANGLE);
1634 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1635 g_signal_connect (obj, "query-tooltip",
1636 G_CALLBACK (on_query_tooltip), NULL);
1637 g_signal_connect (obj, "button-press-event",
1638 G_CALLBACK (on_button_pressed), NULL);
1639 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1641 obj->builder = builder_new ("data-sheet.ui");
1643 action = get_action_assert (obj->builder, "edit_clear-cases");
1644 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1646 gtk_action_set_sensitive (action, FALSE);
1647 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1648 "changed", G_CALLBACK (on_selection_changed), NULL);
1650 action = get_action_assert (obj->builder, "edit_insert-case");
1651 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1654 action = get_action_assert (obj->builder, "edit_insert-variable");
1655 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1658 action = get_action_assert (obj->builder, "edit_goto-case");
1659 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1662 action = get_action_assert (obj->builder, "edit_copy");
1663 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1665 action = get_action_assert (obj->builder, "edit_clear-variables");
1666 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1669 action = get_action_assert (obj->builder, "edit_find");
1670 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1672 action = get_action_assert (obj->builder, "sort-up");
1673 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1675 action = get_action_assert (obj->builder, "sort-down");
1676 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1681 psppire_data_sheet_new (void)
1683 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1687 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1689 return data_sheet->data_store;
1693 refresh_model (PsppireDataSheet *data_sheet)
1695 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1697 if (data_sheet->data_store != NULL)
1699 PsppireEmptyListStore *model;
1703 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1704 model = psppire_empty_list_store_new (n_rows);
1705 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1706 GTK_TREE_MODEL (model));
1707 g_object_unref (model);
1709 action = get_action_assert (data_sheet->builder, "edit_copy");
1710 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1716 on_case_inserted (PsppireDataStore *data_store, gint row,
1717 PsppireDataSheet *data_sheet)
1719 PsppireEmptyListStore *empty_list_store;
1720 GtkTreeModel *tree_model;
1723 g_return_if_fail (data_store == data_sheet->data_store);
1725 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1726 if (row == n_rows - 1)
1729 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1730 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1731 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1732 psppire_empty_list_store_row_inserted (empty_list_store, row);
1736 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1737 PsppireDataSheet *data_sheet)
1740 g_return_if_fail (data_store == data_sheet->data_store);
1744 /* This is a bit of a cop-out. We could do better, if it ever turns out
1745 that this performs too poorly. */
1746 refresh_model (data_sheet);
1750 PsppireEmptyListStore *empty_list_store;
1751 GtkTreeModel *tree_model;
1752 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1754 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1755 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1756 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1757 psppire_empty_list_store_row_deleted (empty_list_store, first);
1762 on_case_change (PsppireDataStore *data_store, gint row,
1763 PsppireDataSheet *data_sheet)
1765 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1767 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1768 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1772 on_backend_changed (PsppireDataStore *data_store,
1773 PsppireDataSheet *data_sheet)
1775 g_return_if_fail (data_store == data_sheet->data_store);
1776 refresh_model (data_sheet);
1780 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1781 PsppireDataSheet *data_sheet)
1783 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1784 PsppSheetViewColumn *column;
1785 struct variable *var;
1789 g_return_if_fail (data_sheet->data_store != NULL);
1790 g_return_if_fail (dict == data_sheet->data_store->dict);
1792 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1797 var = psppire_dict_get_variable (data_store->dict, dict_index);
1798 g_return_if_fail (var != NULL);
1800 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1801 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1802 if (display_width != var_get_display_width (var))
1804 gint base_width, incr_width;
1806 display_width = var_get_display_width (var);
1807 calc_width_conversion (data_sheet, &base_width, &incr_width);
1808 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1809 base_width, incr_width);
1810 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1815 on_variable_changed (PsppireDict *dict, int dict_index,
1816 PsppireDataSheet *data_sheet)
1818 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1819 PsppSheetViewColumn *column;
1820 GtkCellRenderer *cell;
1821 struct variable *var;
1825 g_return_if_fail (data_sheet->data_store != NULL);
1826 g_return_if_fail (dict == data_sheet->data_store->dict);
1828 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1833 var = psppire_dict_get_variable (data_store->dict, dict_index);
1834 g_return_if_fail (var != NULL);
1836 name = escape_underscores (var_get_name (var));
1837 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1838 pspp_sheet_view_column_set_title (column, name);
1841 cells = pspp_sheet_view_column_get_cell_renderers (column);
1842 g_return_if_fail (cells);
1844 g_list_free (cells);
1846 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1848 /* Stop editing before we delete and replace the cell renderers.
1849 Otherwise if this column is currently being edited, an eventual call
1850 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1851 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1853 It's possible that this is a bug in PsppSheetView, and it's possible
1854 that PsppSheetView inherits that from GtkTreeView, but I haven't
1855 investigated yet. */
1856 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1858 add_data_column_cell_renderer (data_sheet, column);
1863 on_variable_inserted (PsppireDict *dict, int var_index,
1864 PsppireDataSheet *data_sheet)
1866 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1867 gint base_width, incr_width;
1868 PsppSheetViewColumn *column;
1870 calc_width_conversion (data_sheet, &base_width, &incr_width);
1871 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1872 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1876 on_variable_deleted (PsppireDict *dict,
1877 const struct variable *var, int case_idx, int width,
1878 PsppireDataSheet *data_sheet)
1880 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1881 GList *columns, *iter;
1883 columns = pspp_sheet_view_get_columns (sheet_view);
1884 for (iter = columns; iter != NULL; iter = iter->next)
1886 PsppSheetViewColumn *column = iter->data;
1887 const struct variable *column_var;
1889 column_var = g_object_get_data (G_OBJECT (column), "variable");
1890 if (column_var == var)
1891 pspp_sheet_view_remove_column (sheet_view, column);
1893 g_list_free (columns);
1897 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1899 PsppireDataStore *store = data_sheet->data_store;
1904 data_sheet->data_store = NULL;
1906 g_signal_handlers_disconnect_by_func (
1907 store, G_CALLBACK (on_backend_changed), data_sheet);
1908 g_signal_handlers_disconnect_by_func (
1909 store, G_CALLBACK (on_case_inserted), data_sheet);
1910 g_signal_handlers_disconnect_by_func (
1911 store, G_CALLBACK (on_cases_deleted), data_sheet);
1912 g_signal_handlers_disconnect_by_func (
1913 store, G_CALLBACK (on_case_change), data_sheet);
1915 g_signal_handlers_disconnect_by_func (
1916 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
1917 g_signal_handlers_disconnect_by_func (
1918 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
1919 g_signal_handlers_disconnect_by_func (
1920 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
1921 g_signal_handlers_disconnect_by_func (
1922 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
1924 g_object_unref (store);
1928 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
1929 PsppireDataStore *data_store)
1931 psppire_data_sheet_unset_data_store (data_sheet);
1933 data_sheet->data_store = data_store;
1934 if (data_store != NULL)
1936 g_object_ref (data_store);
1937 g_signal_connect (data_store, "backend-changed",
1938 G_CALLBACK (on_backend_changed), data_sheet);
1939 g_signal_connect (data_store, "case-inserted",
1940 G_CALLBACK (on_case_inserted), data_sheet);
1941 g_signal_connect (data_store, "cases-deleted",
1942 G_CALLBACK (on_cases_deleted), data_sheet);
1943 g_signal_connect (data_store, "case-changed",
1944 G_CALLBACK (on_case_change), data_sheet);
1946 /* XXX it's unclean to hook into the dict this way--what if the dict
1947 changes? As of this writing, though, nothing ever changes the
1948 data_store's dict. */
1949 g_signal_connect (data_store->dict, "variable-changed",
1950 G_CALLBACK (on_variable_changed),
1952 g_signal_connect (data_store->dict, "variable-display-width-changed",
1953 G_CALLBACK (on_variable_display_width_changed),
1955 g_signal_connect (data_store->dict, "variable-inserted",
1956 G_CALLBACK (on_variable_inserted), data_sheet);
1957 g_signal_connect (data_store->dict, "variable-deleted",
1958 G_CALLBACK (on_variable_deleted), data_sheet);
1960 refresh_model (data_sheet);
1963 /* Clipboard stuff */
1965 /* A casereader and dictionary holding the data currently in the clip */
1966 static struct casereader *clip_datasheet = NULL;
1967 static struct dictionary *clip_dict = NULL;
1970 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
1972 /* Set the clip according to the currently
1973 selected range in the data sheet */
1975 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet)
1977 struct casewriter *writer ;
1978 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
1979 struct case_map *map = NULL;
1980 struct range_set *rows, *cols;
1981 const struct range_set_node *node;
1983 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
1987 /* Destroy any existing clip */
1988 if ( clip_datasheet )
1990 casereader_destroy (clip_datasheet);
1991 clip_datasheet = NULL;
1996 dict_destroy (clip_dict);
2000 /* Construct clip dictionary. */
2001 clip_dict = dict_create (dict_get_encoding (ds->dict->dict));
2002 RANGE_SET_FOR_EACH (node, cols)
2006 for (dict_index = node->start; dict_index < node->end; dict_index++)
2008 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2009 dict_clone_var_assert (clip_dict, var);
2013 /* Construct clip data. */
2014 map = case_map_by_name (ds->dict->dict, clip_dict);
2015 writer = autopaging_writer_create (dict_get_proto (clip_dict));
2016 RANGE_SET_FOR_EACH (node, rows)
2018 unsigned long int row;
2020 for (row = node->start; row < node->end; row++)
2022 struct ccase *old = psppire_data_store_get_case (ds, row);
2024 casewriter_write (writer, case_map_execute (map, old));
2026 casewriter_force_error (writer);
2029 case_map_destroy (map);
2031 range_set_destroy (rows);
2032 range_set_destroy (cols);
2034 clip_datasheet = casewriter_make_reader (writer);
2036 psppire_data_sheet_update_clipboard (data_sheet);
2046 /* Perform data_out for case CC, variable V, appending to STRING */
2048 data_out_g_string (GString *string, const struct variable *v,
2049 const struct ccase *cc)
2051 const struct fmt_spec *fs = var_get_print_format (v);
2052 const union value *val = case_data (cc, v);
2054 char *s = data_out (val, var_get_encoding (v), fs);
2056 g_string_append (string, s);
2067 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2068 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2069 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2071 string = g_string_sized_new (10 * val_cnt * case_cnt);
2073 for (r = 0 ; r < case_cnt ; ++r )
2078 cc = casereader_peek (clip_datasheet, r);
2081 g_warning ("Clipboard seems to have inexplicably shrunk");
2085 for (c = 0 ; c < var_cnt ; ++c)
2087 const struct variable *v = dict_get_var (clip_dict, c);
2088 data_out_g_string (string, v, cc);
2089 if ( c < val_cnt - 1 )
2090 g_string_append (string, "\t");
2094 g_string_append (string, "\n");
2109 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2110 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2111 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2113 /* Guestimate the size needed */
2114 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2116 g_string_append (string,
2117 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2119 g_string_append (string, "<table>\n");
2120 for (r = 0 ; r < case_cnt ; ++r )
2123 struct ccase *cc = casereader_peek (clip_datasheet, r);
2126 g_warning ("Clipboard seems to have inexplicably shrunk");
2129 g_string_append (string, "<tr>\n");
2131 for (c = 0 ; c < var_cnt ; ++c)
2133 const struct variable *v = dict_get_var (clip_dict, c);
2134 g_string_append (string, "<td>");
2135 data_out_g_string (string, v, cc);
2136 g_string_append (string, "</td>\n");
2139 g_string_append (string, "</tr>\n");
2143 g_string_append (string, "</table>\n");
2151 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2152 GtkSelectionData *selection_data,
2156 GString *string = NULL;
2160 case SELECT_FMT_TEXT:
2161 string = clip_to_text ();
2163 case SELECT_FMT_HTML:
2164 string = clip_to_html ();
2167 g_assert_not_reached ();
2170 gtk_selection_data_set (selection_data, selection_data->target,
2172 (const guchar *) string->str, string->len);
2174 g_string_free (string, TRUE);
2178 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2181 dict_destroy (clip_dict);
2184 casereader_destroy (clip_datasheet);
2185 clip_datasheet = NULL;
2189 static const GtkTargetEntry targets[] = {
2190 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2191 { "STRING", 0, SELECT_FMT_TEXT },
2192 { "TEXT", 0, SELECT_FMT_TEXT },
2193 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2194 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2195 { "text/plain", 0, SELECT_FMT_TEXT },
2196 { "text/html", 0, SELECT_FMT_HTML }
2202 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2204 GtkClipboard *clipboard =
2205 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2206 GDK_SELECTION_CLIPBOARD);
2208 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2209 G_N_ELEMENTS (targets),
2210 psppire_data_sheet_clipboard_get_cb,
2211 psppire_data_sheet_clipboard_clear_cb,
2213 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2217 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2219 struct range_set *rows, *cols;
2223 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2226 range_set_destroy (rows);
2227 range_set_destroy (cols);
2230 action = get_action_assert (data_sheet->builder, "edit_copy");
2231 gtk_action_set_sensitive (action, enable);
2233 action = get_action_assert (data_sheet->builder, "edit_cut");
2234 gtk_action_set_sensitive (action, enable);