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 pspp_sheet_view_column_set_selectable (column, TRUE);
227 pspp_sheet_view_column_set_row_head (column, TRUE);
228 pspp_sheet_view_column_set_cell_data_func (
229 column, renderer, render_row_number_cell, ds, NULL);
230 pspp_sheet_view_column_set_fixed_width (column, 50);
231 pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers);
232 pspp_sheet_view_append_column (sheet_view, column);
236 render_data_cell (PsppSheetViewColumn *tree_column,
237 GtkCellRenderer *cell,
240 gpointer data_sheet_)
242 PsppireDataSheet *data_sheet = data_sheet_;
243 PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet);
244 struct variable *var;
250 row = GPOINTER_TO_INT (iter->user_data);
251 var = g_object_get_data (G_OBJECT (tree_column), "variable");
253 string = psppire_data_store_get_string (store, row, var,
254 data_sheet->show_value_labels);
257 GValue gvalue = { 0 };
259 g_value_init (&gvalue, G_TYPE_STRING);
260 g_value_take_string (&gvalue, string);
261 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
262 g_value_unset (&gvalue);
265 g_object_set (G_OBJECT (cell), "text", "", NULL);
267 switch (var_get_alignment (var))
269 case ALIGN_LEFT: xalign = 0.0; break;
270 case ALIGN_RIGHT: xalign = 1.0; break;
271 case ALIGN_CENTRE: xalign = 0.5; break;
272 default: xalign = 0.0; break;
281 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
285 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
286 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
287 NULL, NULL, NULL, &width, NULL);
292 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
299 ds_put_byte_multiple (&s, '0', char_cnt);
300 ds_put_byte (&s, ' ');
301 width = get_string_width (treeview, renderer, ds_cstr (&s));
308 on_data_column_editing_started (GtkCellRenderer *cell,
309 GtkCellEditable *editable,
313 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
314 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
315 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
316 struct variable *var;
318 g_return_if_fail (column);
319 g_return_if_fail (data_sheet);
320 g_return_if_fail (data_store);
323 g_object_ref (editable);
324 g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable",
325 editable, g_object_unref);
327 var = g_object_get_data (G_OBJECT (column), "variable");
328 g_return_if_fail (var);
330 if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable))
332 const struct val_labs *labels = var_get_value_labels (var);
333 const struct val_lab *vl;
334 GtkListStore *list_store;
336 list_store = gtk_list_store_new (1, G_TYPE_STRING);
337 for (vl = val_labs_first (labels); vl != NULL;
338 vl = val_labs_next (labels, vl))
342 gtk_list_store_append (list_store, &iter);
343 gtk_list_store_set (list_store, &iter,
344 0, val_lab_get_label (vl),
348 gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
349 GTK_TREE_MODEL (list_store));
350 g_object_unref (list_store);
355 scroll_to_bottom (GtkWidget *widget,
356 GtkRequisition *requisition,
357 gpointer unused UNUSED)
359 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
360 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
361 GtkAdjustment *vadjust;
363 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
364 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
366 if (data_sheet->scroll_to_bottom_signal)
368 g_signal_handler_disconnect (data_sheet,
369 data_sheet->scroll_to_bottom_signal);
370 data_sheet->scroll_to_bottom_signal = 0;
375 on_data_column_edited (GtkCellRendererText *cell,
380 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
381 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
382 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
383 GtkEditable *editable;
384 struct variable *var;
390 path = gtk_tree_path_new_from_string (path_string);
391 row = gtk_tree_path_get_indices (path)[0];
392 gtk_tree_path_free (path);
394 var = g_object_get_data (G_OBJECT (column), "variable");
396 new_row = row == psppire_data_store_get_case_count (data_store);
397 if (new_row && new_text[0] == '\0')
400 editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
401 g_return_if_fail (editable != NULL);
402 is_val_lab = (GTK_IS_COMBO_BOX (editable)
403 && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
404 g_object_unref (editable);
406 psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
408 if (new_row && !data_sheet->scroll_to_bottom_signal)
410 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
411 data_sheet->scroll_to_bottom_signal =
412 g_signal_connect (data_sheet, "size-request",
413 G_CALLBACK (scroll_to_bottom), NULL);
417 /* We could be more specific about what to redraw, if it seems
418 important for performance. */
419 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
424 scroll_to_right (GtkWidget *widget,
425 PsppireDataSheet *data_sheet)
427 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
428 PsppSheetViewColumn *column, *prev;
429 GList *columns, *iter;
433 columns = pspp_sheet_view_get_columns (sheet_view);
434 for (iter = columns; iter; iter = iter->next)
436 PsppSheetViewColumn *c = iter->data;
437 if (g_object_get_data (G_OBJECT (c), "new-var-column"))
444 g_list_free (columns);
449 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
455 pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
458 pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
459 gtk_tree_path_free (path);
463 if (data_sheet->scroll_to_right_signal)
465 g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
466 data_sheet->scroll_to_right_signal = 0;
471 on_new_variable_column_edited (GtkCellRendererText *cell,
476 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
477 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
478 PsppireDict *dict = data_store->dict;
479 struct variable *var;
484 if (new_text[0] == '\0')
486 /* User didn't enter anything so don't create a variable. */
490 path = gtk_tree_path_new_from_string (path_string);
491 row = gtk_tree_path_get_indices (path)[0];
492 gtk_tree_path_free (path);
494 if (!psppire_dict_generate_name (dict, name, sizeof name))
497 var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
499 g_return_if_fail (var != NULL);
501 psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
503 if (!data_sheet->scroll_to_right_signal)
505 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
506 data_sheet->scroll_to_right_signal =
507 g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
508 G_CALLBACK (scroll_to_right), data_sheet);
512 /* We could be more specific about what to redraw, if it seems
513 important for performance. */
514 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
519 calc_width_conversion (PsppireDataSheet *data_sheet,
520 gint *base_width, gint *incr_width)
522 GtkCellRenderer *cell;
525 cell = gtk_cell_renderer_text_new ();
526 w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
527 w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
528 *incr_width = MAX (1, (w10 - w1) / 9);
529 *base_width = MAX (0, w10 - *incr_width * 10);
530 g_object_ref_sink (cell);
531 g_object_unref (cell);
535 display_width_from_pixel_width (PsppireDataSheet *data_sheet,
538 gint base_width, incr_width;
540 calc_width_conversion (data_sheet, &base_width, &incr_width);
541 return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
545 display_width_to_pixel_width (PsppireDataSheet *data_sheet,
550 return base_width + incr_width * display_width;
554 on_data_column_resized (GObject *gobject,
558 PsppireDataSheet *data_sheet = user_data;
559 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
560 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
561 struct variable *var;
565 if (data_store == NULL)
568 pixel_width = pspp_sheet_view_column_get_width (column);
569 if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
571 /* Short-circuit the expensive display_width_from_pixel_width()
572 calculation, to make loading .sav files with 2000 columns visibly
577 var = g_object_get_data (G_OBJECT (column), "variable");
578 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
579 var_set_display_width (var, display_width);
583 do_data_column_popup_menu (PsppSheetViewColumn *column,
584 guint button, guint32 time)
586 GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
587 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
590 menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
591 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
595 on_data_column_popup_menu (PsppSheetViewColumn *column,
596 gpointer user_data UNUSED)
598 do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
602 on_column_button_press_event (PsppSheetViewColumn *column,
603 GdkEventButton *event,
604 gpointer user_data UNUSED)
606 PsppSheetSelection *selection;
607 PsppSheetView *sheet_view;
609 sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
611 g_return_val_if_fail (sheet_view != NULL, FALSE);
613 selection = pspp_sheet_view_get_selection (sheet_view);
614 g_return_val_if_fail (selection != NULL, FALSE);
616 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
618 do_data_column_popup_menu (column, event->button, event->time);
621 else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
623 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
624 struct variable *var;
626 var = g_object_get_data (G_OBJECT (column), "variable");
631 g_signal_emit_by_name (data_sheet, "var-double-clicked",
632 var_get_dict_index (var), &handled);
641 on_data_column_query_tooltip (PsppSheetViewColumn *column,
643 gpointer user_data UNUSED)
645 struct variable *var;
648 var = g_object_get_data (G_OBJECT (column), "variable");
649 g_return_val_if_fail (var != NULL, FALSE);
651 text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
652 gtk_tooltip_set_text (tooltip, text);
658 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
659 PsppSheetViewColumn *column)
661 GtkCellRenderer *cell;
662 struct variable *var;
664 var = g_object_get_data (G_OBJECT (column), "variable");
665 g_return_if_fail (var != NULL);
667 if (var_has_value_labels (var))
669 cell = gtk_cell_renderer_combo_new ();
670 g_object_set (G_OBJECT (cell),
676 cell = gtk_cell_renderer_text_new ();
678 g_signal_connect (cell, "editing-started",
679 G_CALLBACK (on_data_column_editing_started), NULL);
680 g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
682 g_object_set_data (G_OBJECT (cell), "column", column);
683 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
685 pspp_sheet_view_column_clear (column);
686 pspp_sheet_view_column_pack_start (column, cell, TRUE);
688 pspp_sheet_view_column_set_cell_data_func (
689 column, cell, render_data_cell, data_sheet, NULL);
692 static PsppSheetViewColumn *
693 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
694 gint base_width, gint incr_width)
696 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
697 struct variable *var;
698 PsppSheetViewColumn *column;
702 var = psppire_dict_get_variable (data_store->dict, dict_idx);
704 column = pspp_sheet_view_column_new ();
706 name = escape_underscores (var_get_name (var));
707 pspp_sheet_view_column_set_title (column, name);
710 g_object_set_data (G_OBJECT (column), "variable", var);
712 width = display_width_to_pixel_width (data_sheet,
713 var_get_display_width (var),
714 base_width, incr_width);
715 pspp_sheet_view_column_set_min_width (column, 10);
716 pspp_sheet_view_column_set_fixed_width (column, width);
717 pspp_sheet_view_column_set_resizable (column, TRUE);
719 pspp_sheet_view_column_set_clickable (column, TRUE);
720 g_signal_connect (column, "notify::width",
721 G_CALLBACK (on_data_column_resized), data_sheet);
723 g_signal_connect (column, "button-press-event",
724 G_CALLBACK (on_column_button_press_event),
726 g_signal_connect (column, "query-tooltip",
727 G_CALLBACK (on_data_column_query_tooltip), NULL);
728 g_signal_connect (column, "popup-menu",
729 G_CALLBACK (on_data_column_popup_menu), data_sheet);
731 add_data_column_cell_renderer (data_sheet, column);
737 make_new_variable_column (PsppireDataSheet *data_sheet,
738 gint base_width, gint incr_width)
740 PsppSheetViewColumn *column;
741 GtkCellRenderer *cell;
744 cell = gtk_cell_renderer_text_new ();
745 g_object_set (cell, "editable", TRUE, NULL);
747 g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
750 column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
751 g_object_set_data (G_OBJECT (column), "new-var-column", column);
753 width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
754 pspp_sheet_view_column_set_min_width (column, 10);
755 pspp_sheet_view_column_set_fixed_width (column, width);
757 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
758 g_signal_connect (column, "button-press-event",
759 G_CALLBACK (on_column_button_press_event),
761 g_signal_connect (column, "popup-menu",
762 G_CALLBACK (on_data_column_popup_menu), data_sheet);
764 pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
766 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
767 data_sheet->new_variable_column = column;
771 psppire_data_sheet_model_changed (GObject *gobject,
775 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
776 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
777 PsppireDataStore *data_store;
779 /* Remove old columns. */
782 PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
786 pspp_sheet_view_remove_column (sheet_view, column);
788 data_sheet->new_variable_column = NULL;
790 if (pspp_sheet_view_get_model (sheet_view) == NULL)
792 /* Don't create any columns at all if there's no model. Otherwise we'll
793 create some columns as part of the "dispose" callback for the sheet
794 view, which sets the model to NULL. That causes warnings to be
795 logged and is obviously undesirable in any case. */
799 /* Add new columns. */
800 data_store = psppire_data_sheet_get_data_store (data_sheet);
801 if (data_store != NULL)
803 gint base_width, incr_width;
806 calc_width_conversion (data_sheet, &base_width, &incr_width);
808 make_row_number_column (data_sheet, data_store);
809 for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
811 PsppSheetViewColumn *column;
813 column = make_data_column (data_sheet, i, base_width, incr_width);
814 pspp_sheet_view_append_column (sheet_view, column);
816 make_new_variable_column (data_sheet, base_width, incr_width);
827 PROP_MAY_CREATE_VARS,
828 PROP_MAY_DELETE_VARS,
833 psppire_data_sheet_set_property (GObject *object,
838 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
842 case PROP_DATA_STORE:
843 psppire_data_sheet_set_data_store (
844 obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
847 case PROP_VALUE_LABELS:
848 psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
851 case PROP_CASE_NUMBERS:
852 psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
855 case PROP_CURRENT_CASE:
856 psppire_data_sheet_goto_case (obj, g_value_get_long (value));
859 case PROP_MAY_CREATE_VARS:
860 psppire_data_sheet_set_may_create_vars (obj,
861 g_value_get_boolean (value));
864 case PROP_MAY_DELETE_VARS:
865 psppire_data_sheet_set_may_delete_vars (obj,
866 g_value_get_boolean (value));
869 case PROP_UI_MANAGER:
871 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
877 psppire_data_sheet_get_property (GObject *object,
882 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
886 case PROP_DATA_STORE:
887 g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
890 case PROP_VALUE_LABELS:
891 g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
894 case PROP_CASE_NUMBERS:
895 g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
898 case PROP_CURRENT_CASE:
899 g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
902 case PROP_MAY_CREATE_VARS:
903 g_value_set_boolean (value, obj->may_create_vars);
906 case PROP_MAY_DELETE_VARS:
907 g_value_set_boolean (value, obj->may_delete_vars);
910 case PROP_UI_MANAGER:
911 g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
915 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
921 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
923 return ds->show_value_labels;
927 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
928 gboolean show_value_labels)
930 show_value_labels = !!show_value_labels;
931 if (show_value_labels != ds->show_value_labels)
933 ds->show_value_labels = show_value_labels;
934 g_object_notify (G_OBJECT (ds), "value-labels");
935 gtk_widget_queue_draw (GTK_WIDGET (ds));
937 /* Make the cell being edited refresh too. */
938 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (ds), TRUE);
943 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
945 return ds->show_case_numbers;
949 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
950 gboolean show_case_numbers)
952 show_case_numbers = !!show_case_numbers;
953 if (show_case_numbers != ds->show_case_numbers)
955 PsppSheetViewColumn *column;
957 ds->show_case_numbers = show_case_numbers;
958 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
960 pspp_sheet_view_column_set_visible (column, show_case_numbers);
962 g_object_notify (G_OBJECT (ds), "case-numbers");
963 gtk_widget_queue_draw (GTK_WIDGET (ds));
968 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
970 return data_sheet->may_create_vars;
974 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
975 gboolean may_create_vars)
977 if (data_sheet->may_create_vars != may_create_vars)
979 data_sheet->may_create_vars = may_create_vars;
980 if (data_sheet->new_variable_column)
981 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
984 on_selection_changed (pspp_sheet_view_get_selection (
985 PSPP_SHEET_VIEW (data_sheet)), NULL);
990 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
992 return data_sheet->may_delete_vars;
996 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
997 gboolean may_delete_vars)
999 if (data_sheet->may_delete_vars != may_delete_vars)
1001 data_sheet->may_delete_vars = may_delete_vars;
1002 on_selection_changed (pspp_sheet_view_get_selection (
1003 PSPP_SHEET_VIEW (data_sheet)), NULL);
1007 static PsppSheetViewColumn *
1008 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1011 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1012 PsppireDataStore *data_store;
1013 PsppSheetViewColumn *column;
1014 struct variable *var;
1017 data_store = psppire_data_sheet_get_data_store (data_sheet);
1018 g_return_val_if_fail (data_store != NULL, NULL);
1019 g_return_val_if_fail (data_store->dict != NULL, NULL);
1021 var = psppire_dict_get_variable (data_store->dict, dict_index);
1022 g_return_val_if_fail (var != NULL, NULL);
1025 list = pspp_sheet_view_get_columns (sheet_view);
1026 for (iter = list; iter != NULL; iter = iter->next)
1028 PsppSheetViewColumn *c = iter->data;
1031 v = g_object_get_data (G_OBJECT (c), "variable");
1044 psppire_data_sheet_show_variable (PsppireDataSheet *data_sheet,
1047 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1048 PsppSheetViewColumn *column;
1050 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1053 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column,
1058 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1060 PsppSheetSelection *selection;
1061 struct variable *var;
1062 GList *selected_columns;
1065 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1066 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1069 for (iter = selected_columns; iter != NULL; iter = iter->next)
1071 PsppSheetViewColumn *column = iter->data;
1072 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1085 g_list_free (selected_columns);
1091 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1093 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1094 PsppireDataStore *store = data_sheet->data_store;
1095 PsppSheetSelection *selection;
1098 g_return_if_fail (case_index >= 0);
1099 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1101 path = gtk_tree_path_new_from_indices (case_index, -1);
1103 /* Select the case. */
1104 selection = pspp_sheet_view_get_selection (sheet_view);
1105 pspp_sheet_selection_unselect_all (selection);
1106 pspp_sheet_selection_select_path (selection, path);
1107 pspp_sheet_selection_select_all_columns (selection);
1109 /* Scroll so that the case is visible. */
1110 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1112 gtk_tree_path_free (path);
1115 /* Returns the 0-based index of a selected case, if there is at least one, and
1118 If more than one case is selected, returns the one with the smallest index,
1119 that is, the index of the case closest to the beginning of the file. The
1120 row that can be used to insert a new case is not considered a case. */
1122 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1124 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1125 PsppireDataStore *store = data_sheet->data_store;
1126 const struct range_set_node *node;
1127 PsppSheetSelection *selection;
1128 struct range_set *rows;
1131 selection = pspp_sheet_view_get_selection (sheet_view);
1132 rows = pspp_sheet_selection_get_range_set (selection);
1133 node = range_set_first (rows);
1134 row = (node && node->start < psppire_data_store_get_case_count (store)
1137 range_set_destroy (rows);
1142 /* Returns the 0-based index of a selected case, if exactly one case is
1143 selected, and -1 otherwise. Returns -1 if the row that can be used to
1144 insert a new case is selected. */
1146 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1148 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1149 PsppireDataStore *store = data_sheet->data_store;
1150 const struct range_set_node *node;
1151 PsppSheetSelection *selection;
1152 struct range_set *rows;
1155 selection = pspp_sheet_view_get_selection (sheet_view);
1156 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1159 rows = pspp_sheet_selection_get_range_set (selection);
1160 node = range_set_first (rows);
1161 row = (node && node->start < psppire_data_store_get_case_count (store)
1164 range_set_destroy (rows);
1170 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1172 if (data_sheet->uim == NULL)
1175 GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1177 GTK_TYPE_UI_MANAGER));
1178 g_object_ref (data_sheet->uim);
1181 return data_sheet->uim;
1185 psppire_data_sheet_dispose (GObject *object)
1187 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1189 if (data_sheet->dispose_has_run)
1192 data_sheet->dispose_has_run = TRUE;
1194 psppire_data_sheet_unset_data_store (data_sheet);
1196 g_object_unref (data_sheet->builder);
1198 if (data_sheet->uim)
1199 g_object_unref (data_sheet->uim);
1201 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1205 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1207 GObjectClass *gobject_class;
1209 gobject_class = G_OBJECT_CLASS (class);
1210 gobject_class->set_property = psppire_data_sheet_set_property;
1211 gobject_class->get_property = psppire_data_sheet_get_property;
1213 gobject_class->dispose = psppire_data_sheet_dispose;
1215 g_signal_new ("var-double-clicked",
1216 G_OBJECT_CLASS_TYPE (gobject_class),
1219 g_signal_accumulator_true_handled, NULL,
1220 psppire_marshal_BOOLEAN__INT,
1221 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1223 g_object_class_install_property (
1224 gobject_class, PROP_DATA_STORE,
1225 g_param_spec_object ("data-store",
1227 "The data store for the data sheet to display.",
1228 PSPPIRE_TYPE_DATA_STORE,
1229 G_PARAM_WRITABLE | G_PARAM_READABLE));
1231 g_object_class_install_property (
1232 gobject_class, PROP_VALUE_LABELS,
1233 g_param_spec_boolean ("value-labels",
1235 "Whether or not the data sheet should display labels instead of values",
1237 G_PARAM_WRITABLE | G_PARAM_READABLE));
1239 g_object_class_install_property (
1240 gobject_class, PROP_CASE_NUMBERS,
1241 g_param_spec_boolean ("case-numbers",
1243 "Whether or not the data sheet should display case numbers",
1245 G_PARAM_WRITABLE | G_PARAM_READABLE));
1247 g_object_class_install_property (
1250 g_param_spec_long ("current-case",
1252 "Zero based number of the selected case",
1255 G_PARAM_WRITABLE | G_PARAM_READABLE));
1257 g_object_class_install_property (
1259 PROP_MAY_CREATE_VARS,
1260 g_param_spec_boolean ("may-create-vars",
1261 "May create variables",
1262 "Whether the user may create more variables",
1264 G_PARAM_READWRITE));
1266 g_object_class_install_property (
1268 PROP_MAY_DELETE_VARS,
1269 g_param_spec_boolean ("may-delete-vars",
1270 "May delete variables",
1271 "Whether the user may delete variables",
1273 G_PARAM_READWRITE));
1275 g_object_class_install_property (
1278 g_param_spec_object ("ui-manager",
1280 "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.",
1281 GTK_TYPE_UI_MANAGER,
1286 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1288 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1291 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1292 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1296 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1298 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1302 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1303 gpointer user_data UNUSED)
1305 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1307 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1309 PsppSheetSelection *selection;
1311 selection = pspp_sheet_view_get_selection (sheet_view);
1312 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1316 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1317 &path, NULL, NULL, NULL))
1319 pspp_sheet_selection_unselect_all (selection);
1320 pspp_sheet_selection_select_path (selection, path);
1321 pspp_sheet_selection_select_all_columns (selection);
1322 gtk_tree_path_free (path);
1326 do_popup_menu (widget, event->button, event->time);
1335 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1337 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1338 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1339 const struct range_set_node *node;
1340 struct range_set *selected;
1342 selected = pspp_sheet_selection_get_range_set (selection);
1343 for (node = range_set_last (selected); node != NULL;
1344 node = range_set_prev (selected, node))
1346 unsigned long int start = range_set_node_get_start (node);
1347 unsigned long int count = range_set_node_get_width (node);
1349 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1351 range_set_destroy (selected);
1355 on_selection_changed (PsppSheetSelection *selection,
1356 gpointer user_data UNUSED)
1358 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1359 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1360 gint n_selected_rows;
1361 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1366 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1368 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1369 gtk_action_set_sensitive (action, n_selected_rows > 0);
1371 switch (n_selected_rows)
1374 may_delete_cases = FALSE;
1378 /* The row used for inserting new cases cannot be deleted. */
1379 path = gtk_tree_path_new_from_indices (
1380 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1381 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1383 gtk_tree_path_free (path);
1387 may_delete_cases = TRUE;
1390 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1391 gtk_action_set_sensitive (action, may_delete_cases);
1393 may_delete_vars = may_insert_vars = FALSE;
1394 list = pspp_sheet_selection_get_selected_columns (selection);
1395 for (iter = list; iter != NULL; iter = iter->next)
1397 PsppSheetViewColumn *column = iter->data;
1398 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1402 may_delete_vars = may_insert_vars = TRUE;
1405 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1406 may_insert_vars = TRUE;
1410 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1411 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1413 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1414 gtk_action_set_sensitive (action, may_insert_vars);
1416 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1417 gtk_action_set_sensitive (action, may_delete_vars);
1419 action = get_action_assert (data_sheet->builder, "sort-up");
1420 gtk_action_set_sensitive (action, may_delete_vars);
1422 action = get_action_assert (data_sheet->builder, "sort-down");
1423 gtk_action_set_sensitive (action, may_delete_vars);
1425 psppire_data_sheet_update_clip_actions (data_sheet);
1429 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1430 struct range_set **rowsp,
1431 struct range_set **colsp)
1433 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1434 PsppireDataStore *data_store = data_sheet->data_store;
1435 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1436 unsigned long n_cases;
1437 struct range_set *rows, *cols;
1440 if (data_store == NULL)
1442 n_cases = psppire_data_store_get_case_count (data_store);
1444 rows = pspp_sheet_selection_get_range_set (selection);
1445 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1446 if (range_set_is_empty (rows))
1448 range_set_destroy (rows);
1452 cols = range_set_create ();
1453 list = pspp_sheet_selection_get_selected_columns (selection);
1454 for (iter = list; iter != NULL; iter = iter->next)
1456 PsppSheetViewColumn *column = iter->data;
1457 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1460 range_set_set1 (cols, var_get_dict_index (var), 1);
1463 if (range_set_is_empty (cols))
1465 range_set_destroy (rows);
1466 range_set_destroy (cols);
1476 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1478 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1479 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1480 PsppireDataStore *data_store = data_sheet->data_store;
1481 struct range_set *selected;
1484 selected = pspp_sheet_selection_get_range_set (selection);
1485 row = range_set_scan (selected, 0);
1486 range_set_destroy (selected);
1488 if (row <= psppire_data_store_get_case_count (data_store))
1489 psppire_data_store_insert_new_case (data_store, row);
1493 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1495 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1496 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1497 PsppireDict *dict = data_sheet->data_store->dict;
1498 PsppSheetViewColumn *column;
1499 struct variable *var;
1504 list = pspp_sheet_selection_get_selected_columns (selection);
1507 column = list->data;
1510 var = g_object_get_data (G_OBJECT (column), "variable");
1511 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1512 if (psppire_dict_generate_name (dict, name, sizeof name))
1513 psppire_dict_insert_variable (dict, index, name);
1517 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1519 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1520 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1521 PsppireDict *dict = data_sheet->data_store->dict;
1524 list = pspp_sheet_selection_get_selected_columns (selection);
1527 list = g_list_reverse (list);
1528 for (iter = list; iter; iter = iter->next)
1530 PsppSheetViewColumn *column = iter->data;
1531 struct variable *var;
1533 var = g_object_get_data (G_OBJECT (column), "variable");
1535 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1547 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1549 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1550 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1551 PsppireDataWindow *pdw;
1556 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1557 g_return_if_fail (pdw != NULL);
1559 list = pspp_sheet_selection_get_selected_columns (selection);
1561 syntax = g_string_new ("SORT CASES BY");
1563 for (iter = list; iter; iter = iter->next)
1565 PsppSheetViewColumn *column = iter->data;
1566 struct variable *var;
1568 var = g_object_get_data (G_OBJECT (column), "variable");
1571 g_string_append_printf (syntax, " %s", var_get_name (var));
1577 if (order == SORT_DESCEND)
1578 g_string_append (syntax, " (DOWN)");
1579 g_string_append_c (syntax, '.');
1580 execute_const_syntax_string (pdw, syntax->str);
1582 g_string_free (syntax, TRUE);
1586 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1588 do_sort (data_sheet, SORT_ASCEND);
1592 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1594 do_sort (data_sheet, SORT_DESCEND);
1598 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1600 goto_case_dialog (data_sheet);
1604 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1606 PsppireDataWindow *pdw;
1608 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1609 g_return_if_fail (pdw != NULL);
1615 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1617 psppire_data_sheet_set_clip (data_sheet);
1621 psppire_data_sheet_init (PsppireDataSheet *obj)
1623 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1626 obj->show_value_labels = FALSE;
1627 obj->show_case_numbers = TRUE;
1628 obj->may_create_vars = TRUE;
1629 obj->may_delete_vars = TRUE;
1631 obj->scroll_to_bottom_signal = 0;
1632 obj->scroll_to_right_signal = 0;
1633 obj->new_variable_column = NULL;
1634 obj->container = NULL;
1637 obj->dispose_has_run = FALSE;
1639 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1641 g_signal_connect (obj, "notify::model",
1642 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1644 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1645 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1646 PSPP_SHEET_SELECTION_RECTANGLE);
1648 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1649 g_signal_connect (obj, "query-tooltip",
1650 G_CALLBACK (on_query_tooltip), NULL);
1651 g_signal_connect (obj, "button-press-event",
1652 G_CALLBACK (on_button_pressed), NULL);
1653 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1655 obj->builder = builder_new ("data-sheet.ui");
1657 action = get_action_assert (obj->builder, "edit_clear-cases");
1658 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1660 gtk_action_set_sensitive (action, FALSE);
1661 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1662 "changed", G_CALLBACK (on_selection_changed), NULL);
1664 action = get_action_assert (obj->builder, "edit_insert-case");
1665 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1668 action = get_action_assert (obj->builder, "edit_insert-variable");
1669 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1672 action = get_action_assert (obj->builder, "edit_goto-case");
1673 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1676 action = get_action_assert (obj->builder, "edit_copy");
1677 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1679 action = get_action_assert (obj->builder, "edit_clear-variables");
1680 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1683 action = get_action_assert (obj->builder, "edit_find");
1684 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1686 action = get_action_assert (obj->builder, "sort-up");
1687 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1689 action = get_action_assert (obj->builder, "sort-down");
1690 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1695 psppire_data_sheet_new (void)
1697 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1701 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1703 return data_sheet->data_store;
1707 refresh_model (PsppireDataSheet *data_sheet)
1709 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1711 if (data_sheet->data_store != NULL)
1713 PsppireEmptyListStore *model;
1717 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1718 model = psppire_empty_list_store_new (n_rows);
1719 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1720 GTK_TREE_MODEL (model));
1721 g_object_unref (model);
1723 action = get_action_assert (data_sheet->builder, "edit_copy");
1724 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1730 on_case_inserted (PsppireDataStore *data_store, gint row,
1731 PsppireDataSheet *data_sheet)
1733 PsppireEmptyListStore *empty_list_store;
1734 GtkTreeModel *tree_model;
1737 g_return_if_fail (data_store == data_sheet->data_store);
1739 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1740 if (row == n_rows - 1)
1743 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1744 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1745 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1746 psppire_empty_list_store_row_inserted (empty_list_store, row);
1750 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1751 PsppireDataSheet *data_sheet)
1754 g_return_if_fail (data_store == data_sheet->data_store);
1758 /* This is a bit of a cop-out. We could do better, if it ever turns out
1759 that this performs too poorly. */
1760 refresh_model (data_sheet);
1764 PsppireEmptyListStore *empty_list_store;
1765 GtkTreeModel *tree_model;
1766 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1768 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1769 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1770 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1771 psppire_empty_list_store_row_deleted (empty_list_store, first);
1776 on_case_change (PsppireDataStore *data_store, gint row,
1777 PsppireDataSheet *data_sheet)
1779 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1781 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1782 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1786 on_backend_changed (PsppireDataStore *data_store,
1787 PsppireDataSheet *data_sheet)
1789 g_return_if_fail (data_store == data_sheet->data_store);
1790 refresh_model (data_sheet);
1794 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1795 PsppireDataSheet *data_sheet)
1797 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1798 PsppSheetViewColumn *column;
1799 struct variable *var;
1803 g_return_if_fail (data_sheet->data_store != NULL);
1804 g_return_if_fail (dict == data_sheet->data_store->dict);
1806 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1811 var = psppire_dict_get_variable (data_store->dict, dict_index);
1812 g_return_if_fail (var != NULL);
1814 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1815 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1816 if (display_width != var_get_display_width (var))
1818 gint base_width, incr_width;
1820 display_width = var_get_display_width (var);
1821 calc_width_conversion (data_sheet, &base_width, &incr_width);
1822 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1823 base_width, incr_width);
1824 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1829 on_variable_changed (PsppireDict *dict, int dict_index,
1830 PsppireDataSheet *data_sheet)
1832 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1833 PsppSheetViewColumn *column;
1834 GtkCellRenderer *cell;
1835 struct variable *var;
1839 g_return_if_fail (data_sheet->data_store != NULL);
1840 g_return_if_fail (dict == data_sheet->data_store->dict);
1842 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1847 var = psppire_dict_get_variable (data_store->dict, dict_index);
1848 g_return_if_fail (var != NULL);
1850 name = escape_underscores (var_get_name (var));
1851 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1852 pspp_sheet_view_column_set_title (column, name);
1855 cells = pspp_sheet_view_column_get_cell_renderers (column);
1856 g_return_if_fail (cells);
1858 g_list_free (cells);
1860 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1862 /* Stop editing before we delete and replace the cell renderers.
1863 Otherwise if this column is currently being edited, an eventual call
1864 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1865 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1867 It's possible that this is a bug in PsppSheetView, and it's possible
1868 that PsppSheetView inherits that from GtkTreeView, but I haven't
1869 investigated yet. */
1870 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1872 add_data_column_cell_renderer (data_sheet, column);
1877 on_variable_inserted (PsppireDict *dict, int var_index,
1878 PsppireDataSheet *data_sheet)
1880 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1881 gint base_width, incr_width;
1882 PsppSheetViewColumn *column;
1884 calc_width_conversion (data_sheet, &base_width, &incr_width);
1885 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1886 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1890 on_variable_deleted (PsppireDict *dict,
1891 const struct variable *var, int case_idx, int width,
1892 PsppireDataSheet *data_sheet)
1894 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1895 GList *columns, *iter;
1897 columns = pspp_sheet_view_get_columns (sheet_view);
1898 for (iter = columns; iter != NULL; iter = iter->next)
1900 PsppSheetViewColumn *column = iter->data;
1901 const struct variable *column_var;
1903 column_var = g_object_get_data (G_OBJECT (column), "variable");
1904 if (column_var == var)
1905 pspp_sheet_view_remove_column (sheet_view, column);
1907 g_list_free (columns);
1911 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1913 PsppireDataStore *store = data_sheet->data_store;
1918 data_sheet->data_store = NULL;
1920 g_signal_handlers_disconnect_by_func (
1921 store, G_CALLBACK (on_backend_changed), data_sheet);
1922 g_signal_handlers_disconnect_by_func (
1923 store, G_CALLBACK (on_case_inserted), data_sheet);
1924 g_signal_handlers_disconnect_by_func (
1925 store, G_CALLBACK (on_cases_deleted), data_sheet);
1926 g_signal_handlers_disconnect_by_func (
1927 store, G_CALLBACK (on_case_change), data_sheet);
1929 g_signal_handlers_disconnect_by_func (
1930 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
1931 g_signal_handlers_disconnect_by_func (
1932 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
1933 g_signal_handlers_disconnect_by_func (
1934 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
1935 g_signal_handlers_disconnect_by_func (
1936 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
1938 g_object_unref (store);
1942 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
1943 PsppireDataStore *data_store)
1945 psppire_data_sheet_unset_data_store (data_sheet);
1947 data_sheet->data_store = data_store;
1948 if (data_store != NULL)
1950 g_object_ref (data_store);
1951 g_signal_connect (data_store, "backend-changed",
1952 G_CALLBACK (on_backend_changed), data_sheet);
1953 g_signal_connect (data_store, "case-inserted",
1954 G_CALLBACK (on_case_inserted), data_sheet);
1955 g_signal_connect (data_store, "cases-deleted",
1956 G_CALLBACK (on_cases_deleted), data_sheet);
1957 g_signal_connect (data_store, "case-changed",
1958 G_CALLBACK (on_case_change), data_sheet);
1960 /* XXX it's unclean to hook into the dict this way--what if the dict
1961 changes? As of this writing, though, nothing ever changes the
1962 data_store's dict. */
1963 g_signal_connect (data_store->dict, "variable-changed",
1964 G_CALLBACK (on_variable_changed),
1966 g_signal_connect (data_store->dict, "variable-display-width-changed",
1967 G_CALLBACK (on_variable_display_width_changed),
1969 g_signal_connect (data_store->dict, "variable-inserted",
1970 G_CALLBACK (on_variable_inserted), data_sheet);
1971 g_signal_connect (data_store->dict, "variable-deleted",
1972 G_CALLBACK (on_variable_deleted), data_sheet);
1974 refresh_model (data_sheet);
1977 /* Clipboard stuff */
1979 /* A casereader and dictionary holding the data currently in the clip */
1980 static struct casereader *clip_datasheet = NULL;
1981 static struct dictionary *clip_dict = NULL;
1984 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
1986 /* Set the clip according to the currently
1987 selected range in the data sheet */
1989 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet)
1991 struct casewriter *writer ;
1992 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
1993 struct case_map *map = NULL;
1994 struct range_set *rows, *cols;
1995 const struct range_set_node *node;
1997 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2001 /* Destroy any existing clip */
2002 if ( clip_datasheet )
2004 casereader_destroy (clip_datasheet);
2005 clip_datasheet = NULL;
2010 dict_destroy (clip_dict);
2014 /* Construct clip dictionary. */
2015 clip_dict = dict_create (dict_get_encoding (ds->dict->dict));
2016 RANGE_SET_FOR_EACH (node, cols)
2020 for (dict_index = node->start; dict_index < node->end; dict_index++)
2022 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2023 dict_clone_var_assert (clip_dict, var);
2027 /* Construct clip data. */
2028 map = case_map_by_name (ds->dict->dict, clip_dict);
2029 writer = autopaging_writer_create (dict_get_proto (clip_dict));
2030 RANGE_SET_FOR_EACH (node, rows)
2032 unsigned long int row;
2034 for (row = node->start; row < node->end; row++)
2036 struct ccase *old = psppire_data_store_get_case (ds, row);
2038 casewriter_write (writer, case_map_execute (map, old));
2040 casewriter_force_error (writer);
2043 case_map_destroy (map);
2045 range_set_destroy (rows);
2046 range_set_destroy (cols);
2048 clip_datasheet = casewriter_make_reader (writer);
2050 psppire_data_sheet_update_clipboard (data_sheet);
2060 /* Perform data_out for case CC, variable V, appending to STRING */
2062 data_out_g_string (GString *string, const struct variable *v,
2063 const struct ccase *cc)
2065 const struct fmt_spec *fs = var_get_print_format (v);
2066 const union value *val = case_data (cc, v);
2068 char *s = data_out (val, var_get_encoding (v), fs);
2070 g_string_append (string, s);
2081 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2082 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2083 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2085 string = g_string_sized_new (10 * val_cnt * case_cnt);
2087 for (r = 0 ; r < case_cnt ; ++r )
2092 cc = casereader_peek (clip_datasheet, r);
2095 g_warning ("Clipboard seems to have inexplicably shrunk");
2099 for (c = 0 ; c < var_cnt ; ++c)
2101 const struct variable *v = dict_get_var (clip_dict, c);
2102 data_out_g_string (string, v, cc);
2103 if ( c < val_cnt - 1 )
2104 g_string_append (string, "\t");
2108 g_string_append (string, "\n");
2123 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2124 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2125 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2127 /* Guestimate the size needed */
2128 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2130 g_string_append (string,
2131 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2133 g_string_append (string, "<table>\n");
2134 for (r = 0 ; r < case_cnt ; ++r )
2137 struct ccase *cc = casereader_peek (clip_datasheet, r);
2140 g_warning ("Clipboard seems to have inexplicably shrunk");
2143 g_string_append (string, "<tr>\n");
2145 for (c = 0 ; c < var_cnt ; ++c)
2147 const struct variable *v = dict_get_var (clip_dict, c);
2148 g_string_append (string, "<td>");
2149 data_out_g_string (string, v, cc);
2150 g_string_append (string, "</td>\n");
2153 g_string_append (string, "</tr>\n");
2157 g_string_append (string, "</table>\n");
2165 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2166 GtkSelectionData *selection_data,
2170 GString *string = NULL;
2174 case SELECT_FMT_TEXT:
2175 string = clip_to_text ();
2177 case SELECT_FMT_HTML:
2178 string = clip_to_html ();
2181 g_assert_not_reached ();
2184 gtk_selection_data_set (selection_data, selection_data->target,
2186 (const guchar *) string->str, string->len);
2188 g_string_free (string, TRUE);
2192 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2195 dict_destroy (clip_dict);
2198 casereader_destroy (clip_datasheet);
2199 clip_datasheet = NULL;
2203 static const GtkTargetEntry targets[] = {
2204 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2205 { "STRING", 0, SELECT_FMT_TEXT },
2206 { "TEXT", 0, SELECT_FMT_TEXT },
2207 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2208 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2209 { "text/plain", 0, SELECT_FMT_TEXT },
2210 { "text/html", 0, SELECT_FMT_HTML }
2216 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2218 GtkClipboard *clipboard =
2219 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2220 GDK_SELECTION_CLIPBOARD);
2222 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2223 G_N_ELEMENTS (targets),
2224 psppire_data_sheet_clipboard_get_cb,
2225 psppire_data_sheet_clipboard_clear_cb,
2227 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2231 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2233 struct range_set *rows, *cols;
2237 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2240 range_set_destroy (rows);
2241 range_set_destroy (cols);
2244 action = get_action_assert (data_sheet->builder, "edit_copy");
2245 gtk_action_set_sensitive (action, enable);
2247 action = get_action_assert (data_sheet->builder, "edit_cut");
2248 gtk_action_set_sensitive (action, enable);