1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "ui/gui/psppire-data-sheet.h"
21 #include "data/case-map.h"
22 #include "data/casereader.h"
23 #include "data/casewriter.h"
24 #include "data/data-out.h"
25 #include "data/datasheet.h"
26 #include "data/format.h"
27 #include "data/value-labels.h"
28 #include "libpspp/intern.h"
29 #include "libpspp/range-set.h"
30 #include "ui/gui/executor.h"
31 #include "ui/gui/find-dialog.h"
32 #include "ui/gui/goto-case-dialog.h"
33 #include "ui/gui/builder-wrapper.h"
34 #include "ui/gui/helper.h"
35 #include "ui/gui/pspp-sheet-selection.h"
36 #include "ui/gui/psppire-cell-renderer-button.h"
37 #include "ui/gui/psppire-data-store.h"
38 #include "ui/gui/psppire-data-window.h"
39 #include "ui/gui/psppire-dialog-action-var-info.h"
40 #include "ui/gui/psppire-empty-list-store.h"
41 #include "ui/gui/psppire-marshal.h"
43 #include "gl/intprops.h"
44 #include "gl/xalloc.h"
47 #define _(msgid) gettext (msgid)
48 #define N_(msgid) msgid
50 static void psppire_data_sheet_dispose (GObject *);
51 static void psppire_data_sheet_unset_data_store (PsppireDataSheet *);
53 static void psppire_data_sheet_update_clip_actions (PsppireDataSheet *);
54 static void psppire_data_sheet_set_clip (PsppireDataSheet *, gboolean cut);
56 static void on_selection_changed (PsppSheetSelection *, gpointer);
57 static void on_owner_change (GtkClipboard *, GdkEventOwnerChange *, gpointer);
58 static void psppire_data_sheet_clip_received_cb (GtkClipboard *,
59 GtkSelectionData *, gpointer);
61 G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, PSPP_TYPE_SHEET_VIEW);
64 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
66 size_t *row, PsppSheetViewColumn **columnp)
68 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
72 PsppSheetViewColumn *tree_column;
73 GtkTreeModel *tree_model;
76 /* Check that WIDGET is really visible on the screen before we
77 do anything else. This is a bug fix for a sticky situation:
78 when text_data_import_assistant() returns, it frees the data
79 necessary to compose the tool tip message, but there may be
80 a tool tip under preparation at that point (even if there is
81 no visible tool tip) that will call back into us a little
82 bit later. Perhaps the correct solution to this problem is
83 to make the data related to the tool tips part of a GObject
84 that only gets destroyed when all references are released,
85 but this solution appears to be effective too. */
86 if (!gtk_widget_get_mapped (widget))
89 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
91 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
92 &path, &tree_column, NULL, NULL))
95 *columnp = tree_column;
97 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
100 tree_model = pspp_sheet_view_get_model (tree_view);
101 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
102 gtk_tree_path_free (path);
106 *row = GPOINTER_TO_INT (iter.user_data);
111 on_query_tooltip (GtkWidget *widget, gint wx, gint wy,
112 gboolean keyboard_mode UNUSED,
113 GtkTooltip *tooltip, gpointer data UNUSED)
115 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
116 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
117 PsppSheetViewColumn *column;
118 struct variable *var;
124 g_return_val_if_fail (data_store != NULL, FALSE);
126 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
129 var = g_object_get_data (G_OBJECT (column), "variable");
132 if (g_object_get_data (G_OBJECT (column), "new-var-column") == NULL)
135 gtk_tooltip_set_text (tooltip,
136 _("Enter a number to add a new variable."));
139 else if (row >= datasheet_get_n_rows (data_store->datasheet))
141 gtk_tooltip_set_text (tooltip, _("Enter a number to add a new case."));
145 width = var_get_width (var);
147 value_init (&v, width);
148 datasheet_get_value (data_store->datasheet, row, var_get_case_index (var),
151 label = var_lookup_value_label (var, &v);
154 if (data_sheet->show_value_labels)
156 char *s = value_to_text (v, var);
157 gtk_tooltip_set_text (tooltip, s);
161 gtk_tooltip_set_text (tooltip, label);
163 value_destroy (&v, width);
165 return label != NULL;
169 render_row_number_cell (PsppSheetViewColumn *tree_column,
170 GtkCellRenderer *cell,
175 PsppireDataStore *store = store_;
176 GValue gvalue = { 0, };
179 row = GPOINTER_TO_INT (iter->user_data);
181 g_value_init (&gvalue, G_TYPE_INT);
182 g_value_set_int (&gvalue, row + 1);
183 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
184 g_value_unset (&gvalue);
186 if (row < datasheet_get_n_rows (store->datasheet))
187 g_object_set (cell, "editable", TRUE, NULL);
189 g_object_set (cell, "editable", FALSE, NULL);
192 "slash", psppire_data_store_filtered (store, row),
197 on_row_number_clicked (PsppireCellRendererButton *button,
199 PsppSheetView *sheet_view)
201 PsppSheetSelection *selection;
204 path = gtk_tree_path_new_from_string (path_string);
206 selection = pspp_sheet_view_get_selection (sheet_view);
207 pspp_sheet_selection_unselect_all (selection);
208 pspp_sheet_selection_select_path (selection, path);
209 pspp_sheet_selection_select_all_columns (selection);
211 gtk_tree_path_free (path);
215 make_row_number_column (PsppireDataSheet *data_sheet,
216 PsppireDataStore *ds)
218 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
219 PsppSheetViewColumn *column;
220 GtkCellRenderer *renderer;
222 renderer = psppire_cell_renderer_button_new ();
223 g_object_set (renderer, "xalign", 1.0, NULL);
224 g_signal_connect (renderer, "clicked", G_CALLBACK (on_row_number_clicked),
227 column = pspp_sheet_view_column_new_with_attributes (_("Case"),
229 pspp_sheet_view_column_set_selectable (column, TRUE);
230 pspp_sheet_view_column_set_row_head (column, TRUE);
231 pspp_sheet_view_column_set_clickable (column, TRUE);
232 pspp_sheet_view_column_set_cell_data_func (
233 column, renderer, render_row_number_cell, ds, NULL);
234 pspp_sheet_view_column_set_fixed_width (column, 50);
235 pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers);
236 pspp_sheet_view_append_column (sheet_view, column);
240 render_data_cell (PsppSheetViewColumn *tree_column,
241 GtkCellRenderer *cell,
244 gpointer data_sheet_)
246 PsppireDataSheet *data_sheet = data_sheet_;
247 PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet);
248 struct variable *var;
254 row = GPOINTER_TO_INT (iter->user_data);
255 var = g_object_get_data (G_OBJECT (tree_column), "variable");
257 string = psppire_data_store_get_string (store, row, var,
258 data_sheet->show_value_labels);
261 GValue gvalue = { 0 };
263 g_value_init (&gvalue, G_TYPE_STRING);
264 g_value_take_string (&gvalue, string);
265 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
266 g_value_unset (&gvalue);
269 g_object_set (G_OBJECT (cell), "text", "", NULL);
271 switch (var_get_alignment (var))
273 case ALIGN_LEFT: xalign = 0.0; break;
274 case ALIGN_RIGHT: xalign = 1.0; break;
275 case ALIGN_CENTRE: xalign = 0.5; break;
276 default: xalign = 0.0; break;
285 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
289 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
290 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
291 NULL, NULL, NULL, &width, NULL);
296 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
303 ds_put_byte_multiple (&s, '0', char_cnt);
304 ds_put_byte (&s, ' ');
305 width = get_string_width (treeview, renderer, ds_cstr (&s));
312 on_data_column_editing_started (GtkCellRenderer *cell,
313 GtkCellEditable *editable,
317 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
318 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
319 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
320 struct variable *var;
322 g_return_if_fail (column);
323 g_return_if_fail (data_sheet);
324 g_return_if_fail (data_store);
327 g_object_ref (editable);
328 g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable",
329 editable, g_object_unref);
331 var = g_object_get_data (G_OBJECT (column), "variable");
332 g_return_if_fail (var);
334 if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable))
336 const struct val_labs *labels = var_get_value_labels (var);
337 const struct val_lab *vl;
338 GtkListStore *list_store;
340 list_store = gtk_list_store_new (1, G_TYPE_STRING);
341 for (vl = val_labs_first (labels); vl != NULL;
342 vl = val_labs_next (labels, vl))
346 gtk_list_store_append (list_store, &iter);
347 gtk_list_store_set (list_store, &iter,
348 0, val_lab_get_label (vl),
352 gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
353 GTK_TREE_MODEL (list_store));
354 g_object_unref (list_store);
359 scroll_to_bottom (GtkWidget *widget,
360 GtkRequisition *requisition,
361 gpointer unused UNUSED)
363 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
364 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
365 GtkAdjustment *vadjust;
367 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
368 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
370 if (data_sheet->scroll_to_bottom_signal)
372 g_signal_handler_disconnect (data_sheet,
373 data_sheet->scroll_to_bottom_signal);
374 data_sheet->scroll_to_bottom_signal = 0;
379 on_data_column_edited (GtkCellRendererText *cell,
384 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
385 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
386 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
387 GtkEditable *editable;
388 struct variable *var;
394 path = gtk_tree_path_new_from_string (path_string);
395 row = gtk_tree_path_get_indices (path)[0];
396 gtk_tree_path_free (path);
398 var = g_object_get_data (G_OBJECT (column), "variable");
400 new_row = row == psppire_data_store_get_case_count (data_store);
401 if (new_row && new_text[0] == '\0')
404 editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
405 g_return_if_fail (editable != NULL);
406 is_val_lab = (GTK_IS_COMBO_BOX (editable)
407 && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
408 g_object_unref (editable);
410 psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
412 if (new_row && !data_sheet->scroll_to_bottom_signal)
414 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
415 data_sheet->scroll_to_bottom_signal =
416 g_signal_connect (data_sheet, "size-request",
417 G_CALLBACK (scroll_to_bottom), NULL);
421 /* We could be more specific about what to redraw, if it seems
422 important for performance. */
423 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
428 scroll_to_right (GtkWidget *widget,
429 PsppireDataSheet *data_sheet)
431 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
432 PsppSheetViewColumn *column, *prev;
433 GList *columns, *iter;
437 columns = pspp_sheet_view_get_columns (sheet_view);
438 for (iter = columns; iter; iter = iter->next)
440 PsppSheetViewColumn *c = iter->data;
441 if (g_object_get_data (G_OBJECT (c), "new-var-column"))
448 g_list_free (columns);
453 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
459 pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
462 pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
463 gtk_tree_path_free (path);
467 if (data_sheet->scroll_to_right_signal)
469 g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
470 data_sheet->scroll_to_right_signal = 0;
475 on_new_variable_column_edited (GtkCellRendererText *cell,
480 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
481 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
482 PsppireDict *dict = data_store->dict;
483 struct variable *var;
488 if (new_text[0] == '\0')
490 /* User didn't enter anything so don't create a variable. */
494 path = gtk_tree_path_new_from_string (path_string);
495 row = gtk_tree_path_get_indices (path)[0];
496 gtk_tree_path_free (path);
498 if (!psppire_dict_generate_name (dict, name, sizeof name))
501 var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
503 g_return_if_fail (var != NULL);
505 psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
507 if (!data_sheet->scroll_to_right_signal)
509 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
510 data_sheet->scroll_to_right_signal =
511 g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
512 G_CALLBACK (scroll_to_right), data_sheet);
516 /* We could be more specific about what to redraw, if it seems
517 important for performance. */
518 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
523 calc_width_conversion (PsppireDataSheet *data_sheet,
524 gint *base_width, gint *incr_width)
526 GtkCellRenderer *cell;
529 cell = gtk_cell_renderer_text_new ();
530 w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
531 w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
532 *incr_width = MAX (1, (w10 - w1) / 9);
533 *base_width = MAX (0, w10 - *incr_width * 10);
534 g_object_ref_sink (cell);
535 g_object_unref (cell);
539 display_width_from_pixel_width (PsppireDataSheet *data_sheet,
542 gint base_width, incr_width;
544 calc_width_conversion (data_sheet, &base_width, &incr_width);
545 return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
549 display_width_to_pixel_width (PsppireDataSheet *data_sheet,
554 return base_width + incr_width * display_width;
558 on_data_column_resized (GObject *gobject,
562 PsppireDataSheet *data_sheet = user_data;
563 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
564 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
565 struct variable *var;
569 if (data_store == NULL)
572 pixel_width = pspp_sheet_view_column_get_width (column);
573 if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
575 /* Short-circuit the expensive display_width_from_pixel_width()
576 calculation, to make loading .sav files with 2000 columns visibly
581 var = g_object_get_data (G_OBJECT (column), "variable");
582 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
583 var_set_display_width (var, display_width);
587 do_data_column_popup_menu (PsppSheetViewColumn *column,
588 guint button, guint32 time)
590 GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
591 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
594 menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
595 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
599 on_data_column_popup_menu (PsppSheetViewColumn *column,
600 gpointer user_data UNUSED)
602 do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
606 on_column_button_press_event (PsppSheetViewColumn *column,
607 GdkEventButton *event,
608 gpointer user_data UNUSED)
610 PsppSheetSelection *selection;
611 PsppSheetView *sheet_view;
613 sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
615 g_return_val_if_fail (sheet_view != NULL, FALSE);
617 selection = pspp_sheet_view_get_selection (sheet_view);
618 g_return_val_if_fail (selection != NULL, FALSE);
620 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
622 do_data_column_popup_menu (column, event->button, event->time);
625 else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
627 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
628 struct variable *var;
630 var = g_object_get_data (G_OBJECT (column), "variable");
635 g_signal_emit_by_name (data_sheet, "var-double-clicked",
636 var_get_dict_index (var), &handled);
645 on_data_column_query_tooltip (PsppSheetViewColumn *column,
647 gpointer user_data UNUSED)
649 struct variable *var;
652 var = g_object_get_data (G_OBJECT (column), "variable");
653 g_return_val_if_fail (var != NULL, FALSE);
655 text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
656 gtk_tooltip_set_text (tooltip, text);
662 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
663 PsppSheetViewColumn *column)
665 GtkCellRenderer *cell;
666 struct variable *var;
668 var = g_object_get_data (G_OBJECT (column), "variable");
669 g_return_if_fail (var != NULL);
671 if (var_has_value_labels (var))
673 cell = gtk_cell_renderer_combo_new ();
674 g_object_set (G_OBJECT (cell),
680 cell = gtk_cell_renderer_text_new ();
682 g_signal_connect (cell, "editing-started",
683 G_CALLBACK (on_data_column_editing_started), NULL);
684 g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
686 g_object_set_data (G_OBJECT (cell), "column", column);
687 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
689 pspp_sheet_view_column_clear (column);
690 pspp_sheet_view_column_pack_start (column, cell, TRUE);
692 pspp_sheet_view_column_set_cell_data_func (
693 column, cell, render_data_cell, data_sheet, NULL);
696 static PsppSheetViewColumn *
697 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
698 gint base_width, gint incr_width)
700 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
701 struct variable *var;
702 PsppSheetViewColumn *column;
706 var = psppire_dict_get_variable (data_store->dict, dict_idx);
708 column = pspp_sheet_view_column_new ();
710 name = escape_underscores (var_get_name (var));
711 pspp_sheet_view_column_set_title (column, name);
714 g_object_set_data (G_OBJECT (column), "variable", var);
716 width = display_width_to_pixel_width (data_sheet,
717 var_get_display_width (var),
718 base_width, incr_width);
719 pspp_sheet_view_column_set_min_width (column, 10);
720 pspp_sheet_view_column_set_fixed_width (column, width);
721 pspp_sheet_view_column_set_resizable (column, TRUE);
723 pspp_sheet_view_column_set_clickable (column, TRUE);
724 g_signal_connect (column, "notify::width",
725 G_CALLBACK (on_data_column_resized), data_sheet);
727 g_signal_connect (column, "button-press-event",
728 G_CALLBACK (on_column_button_press_event),
730 g_signal_connect (column, "query-tooltip",
731 G_CALLBACK (on_data_column_query_tooltip), NULL);
732 g_signal_connect (column, "popup-menu",
733 G_CALLBACK (on_data_column_popup_menu), data_sheet);
735 add_data_column_cell_renderer (data_sheet, column);
741 make_new_variable_column (PsppireDataSheet *data_sheet,
742 gint base_width, gint incr_width)
744 PsppSheetViewColumn *column;
745 GtkCellRenderer *cell;
748 cell = gtk_cell_renderer_text_new ();
749 g_object_set (cell, "editable", TRUE, NULL);
751 g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
754 column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
755 g_object_set_data (G_OBJECT (column), "new-var-column", column);
757 width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
758 pspp_sheet_view_column_set_min_width (column, 10);
759 pspp_sheet_view_column_set_fixed_width (column, width);
761 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
762 g_signal_connect (column, "button-press-event",
763 G_CALLBACK (on_column_button_press_event),
765 g_signal_connect (column, "popup-menu",
766 G_CALLBACK (on_data_column_popup_menu), data_sheet);
768 pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
770 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
771 data_sheet->new_variable_column = column;
775 psppire_data_sheet_model_changed (GObject *gobject,
779 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
780 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
781 PsppireDataStore *data_store;
783 /* Remove old columns. */
786 PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
790 pspp_sheet_view_remove_column (sheet_view, column);
792 data_sheet->new_variable_column = NULL;
794 if (pspp_sheet_view_get_model (sheet_view) == NULL)
796 /* Don't create any columns at all if there's no model. Otherwise we'll
797 create some columns as part of the "dispose" callback for the sheet
798 view, which sets the model to NULL. That causes warnings to be
799 logged and is obviously undesirable in any case. */
803 /* Add new columns. */
804 data_store = psppire_data_sheet_get_data_store (data_sheet);
805 if (data_store != NULL)
807 gint base_width, incr_width;
810 calc_width_conversion (data_sheet, &base_width, &incr_width);
812 make_row_number_column (data_sheet, data_store);
813 for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
815 PsppSheetViewColumn *column;
817 column = make_data_column (data_sheet, i, base_width, incr_width);
818 pspp_sheet_view_append_column (sheet_view, column);
820 make_new_variable_column (data_sheet, base_width, incr_width);
831 PROP_MAY_CREATE_VARS,
832 PROP_MAY_DELETE_VARS,
837 psppire_data_sheet_set_property (GObject *object,
842 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
846 case PROP_DATA_STORE:
847 psppire_data_sheet_set_data_store (
848 obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
851 case PROP_VALUE_LABELS:
852 psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
855 case PROP_CASE_NUMBERS:
856 psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
859 case PROP_CURRENT_CASE:
860 psppire_data_sheet_goto_case (obj, g_value_get_long (value));
863 case PROP_MAY_CREATE_VARS:
864 psppire_data_sheet_set_may_create_vars (obj,
865 g_value_get_boolean (value));
868 case PROP_MAY_DELETE_VARS:
869 psppire_data_sheet_set_may_delete_vars (obj,
870 g_value_get_boolean (value));
873 case PROP_UI_MANAGER:
875 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
881 psppire_data_sheet_get_property (GObject *object,
886 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
890 case PROP_DATA_STORE:
891 g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
894 case PROP_VALUE_LABELS:
895 g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
898 case PROP_CASE_NUMBERS:
899 g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
902 case PROP_CURRENT_CASE:
903 g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
906 case PROP_MAY_CREATE_VARS:
907 g_value_set_boolean (value, obj->may_create_vars);
910 case PROP_MAY_DELETE_VARS:
911 g_value_set_boolean (value, obj->may_delete_vars);
914 case PROP_UI_MANAGER:
915 g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
919 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
925 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
927 return ds->show_value_labels;
931 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
932 gboolean show_value_labels)
934 show_value_labels = !!show_value_labels;
935 if (show_value_labels != ds->show_value_labels)
937 ds->show_value_labels = show_value_labels;
938 g_object_notify (G_OBJECT (ds), "value-labels");
939 gtk_widget_queue_draw (GTK_WIDGET (ds));
941 /* Make the cell being edited refresh too. */
942 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (ds), TRUE);
947 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
949 return ds->show_case_numbers;
953 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
954 gboolean show_case_numbers)
956 show_case_numbers = !!show_case_numbers;
957 if (show_case_numbers != ds->show_case_numbers)
959 PsppSheetViewColumn *column;
961 ds->show_case_numbers = show_case_numbers;
962 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
964 pspp_sheet_view_column_set_visible (column, show_case_numbers);
966 g_object_notify (G_OBJECT (ds), "case-numbers");
967 gtk_widget_queue_draw (GTK_WIDGET (ds));
972 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
974 return data_sheet->may_create_vars;
978 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
979 gboolean may_create_vars)
981 if (data_sheet->may_create_vars != may_create_vars)
983 data_sheet->may_create_vars = may_create_vars;
984 if (data_sheet->new_variable_column)
985 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
988 on_selection_changed (pspp_sheet_view_get_selection (
989 PSPP_SHEET_VIEW (data_sheet)), NULL);
994 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
996 return data_sheet->may_delete_vars;
1000 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
1001 gboolean may_delete_vars)
1003 if (data_sheet->may_delete_vars != may_delete_vars)
1005 data_sheet->may_delete_vars = may_delete_vars;
1006 on_selection_changed (pspp_sheet_view_get_selection (
1007 PSPP_SHEET_VIEW (data_sheet)), NULL);
1011 static PsppSheetViewColumn *
1012 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1015 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1016 PsppireDataStore *data_store;
1017 PsppSheetViewColumn *column;
1018 struct variable *var;
1021 data_store = psppire_data_sheet_get_data_store (data_sheet);
1022 g_return_val_if_fail (data_store != NULL, NULL);
1023 g_return_val_if_fail (data_store->dict != NULL, NULL);
1025 var = psppire_dict_get_variable (data_store->dict, dict_index);
1026 g_return_val_if_fail (var != NULL, NULL);
1029 list = pspp_sheet_view_get_columns (sheet_view);
1030 for (iter = list; iter != NULL; iter = iter->next)
1032 PsppSheetViewColumn *c = iter->data;
1035 v = g_object_get_data (G_OBJECT (c), "variable");
1048 psppire_data_sheet_show_variable (PsppireDataSheet *data_sheet,
1051 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1052 PsppSheetViewColumn *column;
1054 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1057 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column,
1062 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1064 PsppSheetSelection *selection;
1065 struct variable *var;
1066 GList *selected_columns;
1069 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1070 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1073 for (iter = selected_columns; iter != NULL; iter = iter->next)
1075 PsppSheetViewColumn *column = iter->data;
1076 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1089 g_list_free (selected_columns);
1095 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1097 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1098 PsppireDataStore *store = data_sheet->data_store;
1099 PsppSheetSelection *selection;
1102 g_return_if_fail (case_index >= 0);
1103 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1105 path = gtk_tree_path_new_from_indices (case_index, -1);
1107 /* Select the case. */
1108 selection = pspp_sheet_view_get_selection (sheet_view);
1109 pspp_sheet_selection_unselect_all (selection);
1110 pspp_sheet_selection_select_path (selection, path);
1111 pspp_sheet_selection_select_all_columns (selection);
1113 /* Scroll so that the case is visible. */
1114 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1116 gtk_tree_path_free (path);
1119 /* Returns the 0-based index of a selected case, if there is at least one, and
1122 If more than one case is selected, returns the one with the smallest index,
1123 that is, the index of the case closest to the beginning of the file. The
1124 row that can be used to insert a new case is not considered a case. */
1126 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1128 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1129 PsppireDataStore *store = data_sheet->data_store;
1130 const struct range_set_node *node;
1131 PsppSheetSelection *selection;
1132 struct range_set *rows;
1135 selection = pspp_sheet_view_get_selection (sheet_view);
1136 rows = pspp_sheet_selection_get_range_set (selection);
1137 node = range_set_first (rows);
1138 row = (node && node->start < psppire_data_store_get_case_count (store)
1141 range_set_destroy (rows);
1146 /* Returns the 0-based index of a selected case, if exactly one case is
1147 selected, and -1 otherwise. Returns -1 if the row that can be used to
1148 insert a new case is selected. */
1150 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1152 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1153 PsppireDataStore *store = data_sheet->data_store;
1154 const struct range_set_node *node;
1155 PsppSheetSelection *selection;
1156 struct range_set *rows;
1159 selection = pspp_sheet_view_get_selection (sheet_view);
1160 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1163 rows = pspp_sheet_selection_get_range_set (selection);
1164 node = range_set_first (rows);
1165 row = (node && node->start < psppire_data_store_get_case_count (store)
1168 range_set_destroy (rows);
1174 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1176 if (data_sheet->uim == NULL)
1179 GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1181 GTK_TYPE_UI_MANAGER));
1182 g_object_ref (data_sheet->uim);
1185 return data_sheet->uim;
1189 psppire_data_sheet_dispose (GObject *object)
1191 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1193 if (data_sheet->dispose_has_run)
1196 data_sheet->dispose_has_run = TRUE;
1198 psppire_data_sheet_unset_data_store (data_sheet);
1200 g_object_unref (data_sheet->builder);
1202 if (data_sheet->uim)
1203 g_object_unref (data_sheet->uim);
1205 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1209 psppire_data_sheet_map (GtkWidget *widget)
1213 GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
1215 clip = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
1216 g_signal_connect (clip, "owner-change", G_CALLBACK (on_owner_change),
1218 on_owner_change (clip, NULL, widget);
1222 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1224 GObjectClass *gobject_class;
1225 GtkWidgetClass *widget_class;
1227 gobject_class = G_OBJECT_CLASS (class);
1228 gobject_class->set_property = psppire_data_sheet_set_property;
1229 gobject_class->get_property = psppire_data_sheet_get_property;
1230 gobject_class->dispose = psppire_data_sheet_dispose;
1232 widget_class = GTK_WIDGET_CLASS (class);
1233 widget_class->map = psppire_data_sheet_map;
1235 g_signal_new ("var-double-clicked",
1236 G_OBJECT_CLASS_TYPE (gobject_class),
1239 g_signal_accumulator_true_handled, NULL,
1240 psppire_marshal_BOOLEAN__INT,
1241 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1243 g_object_class_install_property (
1244 gobject_class, PROP_DATA_STORE,
1245 g_param_spec_object ("data-store",
1247 "The data store for the data sheet to display.",
1248 PSPPIRE_TYPE_DATA_STORE,
1249 G_PARAM_WRITABLE | G_PARAM_READABLE));
1251 g_object_class_install_property (
1252 gobject_class, PROP_VALUE_LABELS,
1253 g_param_spec_boolean ("value-labels",
1255 "Whether or not the data sheet should display labels instead of values",
1257 G_PARAM_WRITABLE | G_PARAM_READABLE));
1259 g_object_class_install_property (
1260 gobject_class, PROP_CASE_NUMBERS,
1261 g_param_spec_boolean ("case-numbers",
1263 "Whether or not the data sheet should display case numbers",
1265 G_PARAM_WRITABLE | G_PARAM_READABLE));
1267 g_object_class_install_property (
1270 g_param_spec_long ("current-case",
1272 "Zero based number of the selected case",
1275 G_PARAM_WRITABLE | G_PARAM_READABLE));
1277 g_object_class_install_property (
1279 PROP_MAY_CREATE_VARS,
1280 g_param_spec_boolean ("may-create-vars",
1281 "May create variables",
1282 "Whether the user may create more variables",
1284 G_PARAM_READWRITE));
1286 g_object_class_install_property (
1288 PROP_MAY_DELETE_VARS,
1289 g_param_spec_boolean ("may-delete-vars",
1290 "May delete variables",
1291 "Whether the user may delete variables",
1293 G_PARAM_READWRITE));
1295 g_object_class_install_property (
1298 g_param_spec_object ("ui-manager",
1300 "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.",
1301 GTK_TYPE_UI_MANAGER,
1306 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1308 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1311 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1312 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1316 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1318 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1322 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1323 gpointer user_data UNUSED)
1325 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1327 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1329 PsppSheetSelection *selection;
1331 selection = pspp_sheet_view_get_selection (sheet_view);
1332 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1336 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1337 &path, NULL, NULL, NULL))
1339 pspp_sheet_selection_unselect_all (selection);
1340 pspp_sheet_selection_select_path (selection, path);
1341 pspp_sheet_selection_select_all_columns (selection);
1342 gtk_tree_path_free (path);
1346 do_popup_menu (widget, event->button, event->time);
1355 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1357 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1358 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1359 const struct range_set_node *node;
1360 struct range_set *selected;
1362 selected = pspp_sheet_selection_get_range_set (selection);
1363 for (node = range_set_last (selected); node != NULL;
1364 node = range_set_prev (selected, node))
1366 unsigned long int start = range_set_node_get_start (node);
1367 unsigned long int count = range_set_node_get_width (node);
1369 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1371 range_set_destroy (selected);
1375 on_selection_changed (PsppSheetSelection *selection,
1376 gpointer user_data UNUSED)
1378 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1379 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1380 gint n_selected_rows;
1381 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1386 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1388 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1389 gtk_action_set_sensitive (action, n_selected_rows > 0);
1391 switch (n_selected_rows)
1394 may_delete_cases = FALSE;
1398 /* The row used for inserting new cases cannot be deleted. */
1399 path = gtk_tree_path_new_from_indices (
1400 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1401 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1403 gtk_tree_path_free (path);
1407 may_delete_cases = TRUE;
1410 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1411 gtk_action_set_sensitive (action, may_delete_cases);
1413 may_delete_vars = may_insert_vars = FALSE;
1414 list = pspp_sheet_selection_get_selected_columns (selection);
1415 for (iter = list; iter != NULL; iter = iter->next)
1417 PsppSheetViewColumn *column = iter->data;
1418 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1422 may_delete_vars = may_insert_vars = TRUE;
1425 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1426 may_insert_vars = TRUE;
1430 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1431 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1433 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1434 gtk_action_set_sensitive (action, may_insert_vars);
1436 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1437 gtk_action_set_sensitive (action, may_delete_vars);
1439 action = get_action_assert (data_sheet->builder, "sort-up");
1440 gtk_action_set_sensitive (action, may_delete_vars);
1442 action = get_action_assert (data_sheet->builder, "sort-down");
1443 gtk_action_set_sensitive (action, may_delete_vars);
1445 psppire_data_sheet_update_clip_actions (data_sheet);
1449 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1450 struct range_set **rowsp,
1451 struct range_set **colsp)
1453 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1454 PsppireDataStore *data_store = data_sheet->data_store;
1455 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1456 unsigned long n_cases;
1457 struct range_set *rows, *cols;
1460 if (data_store == NULL)
1462 n_cases = psppire_data_store_get_case_count (data_store);
1464 rows = pspp_sheet_selection_get_range_set (selection);
1465 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1466 if (range_set_is_empty (rows))
1468 range_set_destroy (rows);
1472 cols = range_set_create ();
1473 list = pspp_sheet_selection_get_selected_columns (selection);
1474 for (iter = list; iter != NULL; iter = iter->next)
1476 PsppSheetViewColumn *column = iter->data;
1477 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1480 range_set_set1 (cols, var_get_dict_index (var), 1);
1483 if (range_set_is_empty (cols))
1485 range_set_destroy (rows);
1486 range_set_destroy (cols);
1496 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1498 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1499 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1500 PsppireDataStore *data_store = data_sheet->data_store;
1501 struct range_set *selected;
1504 selected = pspp_sheet_selection_get_range_set (selection);
1505 row = range_set_scan (selected, 0);
1506 range_set_destroy (selected);
1508 if (row <= psppire_data_store_get_case_count (data_store))
1509 psppire_data_store_insert_new_case (data_store, row);
1513 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1515 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1516 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1517 PsppireDict *dict = data_sheet->data_store->dict;
1518 PsppSheetViewColumn *column;
1519 struct variable *var;
1524 list = pspp_sheet_selection_get_selected_columns (selection);
1527 column = list->data;
1530 var = g_object_get_data (G_OBJECT (column), "variable");
1531 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1532 if (psppire_dict_generate_name (dict, name, sizeof name))
1533 psppire_dict_insert_variable (dict, index, name);
1537 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1539 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1540 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1541 PsppireDict *dict = data_sheet->data_store->dict;
1544 list = pspp_sheet_selection_get_selected_columns (selection);
1547 list = g_list_reverse (list);
1548 for (iter = list; iter; iter = iter->next)
1550 PsppSheetViewColumn *column = iter->data;
1551 struct variable *var;
1553 var = g_object_get_data (G_OBJECT (column), "variable");
1555 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1567 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1569 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1570 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1571 PsppireDataWindow *pdw;
1576 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1577 g_return_if_fail (pdw != NULL);
1579 list = pspp_sheet_selection_get_selected_columns (selection);
1581 syntax = g_string_new ("SORT CASES BY");
1583 for (iter = list; iter; iter = iter->next)
1585 PsppSheetViewColumn *column = iter->data;
1586 struct variable *var;
1588 var = g_object_get_data (G_OBJECT (column), "variable");
1591 g_string_append_printf (syntax, " %s", var_get_name (var));
1597 if (order == SORT_DESCEND)
1598 g_string_append (syntax, " (DOWN)");
1599 g_string_append_c (syntax, '.');
1600 execute_const_syntax_string (pdw, syntax->str);
1602 g_string_free (syntax, TRUE);
1606 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1608 do_sort (data_sheet, SORT_ASCEND);
1612 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1614 do_sort (data_sheet, SORT_DESCEND);
1618 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1620 goto_case_dialog (data_sheet);
1624 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1626 PsppireDataWindow *pdw;
1628 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1629 g_return_if_fail (pdw != NULL);
1635 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1637 psppire_data_sheet_set_clip (data_sheet, FALSE);
1641 on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet)
1643 psppire_data_sheet_set_clip (data_sheet, TRUE);
1647 on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet)
1649 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
1650 GtkClipboard *clipboard =
1651 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1653 gtk_clipboard_request_contents (clipboard,
1654 gdk_atom_intern ("UTF8_STRING", TRUE),
1655 psppire_data_sheet_clip_received_cb,
1660 psppire_data_sheet_init (PsppireDataSheet *obj)
1662 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1665 obj->show_value_labels = FALSE;
1666 obj->show_case_numbers = TRUE;
1667 obj->may_create_vars = TRUE;
1668 obj->may_delete_vars = TRUE;
1670 obj->scroll_to_bottom_signal = 0;
1671 obj->scroll_to_right_signal = 0;
1672 obj->new_variable_column = NULL;
1673 obj->container = NULL;
1676 obj->dispose_has_run = FALSE;
1678 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1680 g_signal_connect (obj, "notify::model",
1681 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1683 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1684 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1685 PSPP_SHEET_SELECTION_RECTANGLE);
1687 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1688 g_signal_connect (obj, "query-tooltip",
1689 G_CALLBACK (on_query_tooltip), NULL);
1690 g_signal_connect (obj, "button-press-event",
1691 G_CALLBACK (on_button_pressed), NULL);
1692 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1694 obj->builder = builder_new ("data-sheet.ui");
1696 action = get_action_assert (obj->builder, "edit_clear-cases");
1697 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1699 gtk_action_set_sensitive (action, FALSE);
1700 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1701 "changed", G_CALLBACK (on_selection_changed), NULL);
1703 action = get_action_assert (obj->builder, "edit_insert-case");
1704 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1707 action = get_action_assert (obj->builder, "edit_insert-variable");
1708 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1711 action = get_action_assert (obj->builder, "edit_goto-case");
1712 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1715 action = get_action_assert (obj->builder, "edit_copy");
1716 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1718 action = get_action_assert (obj->builder, "edit_cut");
1719 g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj);
1721 action = get_action_assert (obj->builder, "edit_paste");
1722 g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj);
1724 action = get_action_assert (obj->builder, "edit_clear-variables");
1725 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1728 action = get_action_assert (obj->builder, "edit_find");
1729 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1731 action = get_action_assert (obj->builder, "sort-up");
1732 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1734 action = get_action_assert (obj->builder, "sort-down");
1735 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1740 psppire_data_sheet_new (void)
1742 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1746 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1748 return data_sheet->data_store;
1752 refresh_model (PsppireDataSheet *data_sheet)
1754 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1756 if (data_sheet->data_store != NULL)
1758 PsppireEmptyListStore *model;
1762 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1763 model = psppire_empty_list_store_new (n_rows);
1764 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1765 GTK_TREE_MODEL (model));
1766 g_object_unref (model);
1768 action = get_action_assert (data_sheet->builder, "edit_copy");
1769 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1775 on_case_inserted (PsppireDataStore *data_store, gint row,
1776 PsppireDataSheet *data_sheet)
1778 PsppireEmptyListStore *empty_list_store;
1779 GtkTreeModel *tree_model;
1782 g_return_if_fail (data_store == data_sheet->data_store);
1784 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1785 if (row == n_rows - 1)
1788 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1789 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1790 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1791 psppire_empty_list_store_row_inserted (empty_list_store, row);
1795 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1796 PsppireDataSheet *data_sheet)
1799 g_return_if_fail (data_store == data_sheet->data_store);
1803 /* This is a bit of a cop-out. We could do better, if it ever turns out
1804 that this performs too poorly. */
1805 refresh_model (data_sheet);
1809 PsppireEmptyListStore *empty_list_store;
1810 GtkTreeModel *tree_model;
1811 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1813 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1814 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1815 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1816 psppire_empty_list_store_row_deleted (empty_list_store, first);
1821 on_case_change (PsppireDataStore *data_store, gint row,
1822 PsppireDataSheet *data_sheet)
1824 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1826 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1827 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1831 on_backend_changed (PsppireDataStore *data_store,
1832 PsppireDataSheet *data_sheet)
1834 g_return_if_fail (data_store == data_sheet->data_store);
1835 refresh_model (data_sheet);
1839 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1840 PsppireDataSheet *data_sheet)
1842 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1843 PsppSheetViewColumn *column;
1844 struct variable *var;
1848 g_return_if_fail (data_sheet->data_store != NULL);
1849 g_return_if_fail (dict == data_sheet->data_store->dict);
1851 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1856 var = psppire_dict_get_variable (data_store->dict, dict_index);
1857 g_return_if_fail (var != NULL);
1859 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1860 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1861 if (display_width != var_get_display_width (var))
1863 gint base_width, incr_width;
1865 display_width = var_get_display_width (var);
1866 calc_width_conversion (data_sheet, &base_width, &incr_width);
1867 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1868 base_width, incr_width);
1869 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1874 on_variable_changed (PsppireDict *dict, int dict_index,
1875 PsppireDataSheet *data_sheet)
1877 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1878 PsppSheetViewColumn *column;
1879 GtkCellRenderer *cell;
1880 struct variable *var;
1884 g_return_if_fail (data_sheet->data_store != NULL);
1885 g_return_if_fail (dict == data_sheet->data_store->dict);
1887 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1892 var = psppire_dict_get_variable (data_store->dict, dict_index);
1893 g_return_if_fail (var != NULL);
1895 name = escape_underscores (var_get_name (var));
1896 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1897 pspp_sheet_view_column_set_title (column, name);
1900 cells = pspp_sheet_view_column_get_cell_renderers (column);
1901 g_return_if_fail (cells);
1903 g_list_free (cells);
1905 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1907 /* Stop editing before we delete and replace the cell renderers.
1908 Otherwise if this column is currently being edited, an eventual call
1909 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1910 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1912 It's possible that this is a bug in PsppSheetView, and it's possible
1913 that PsppSheetView inherits that from GtkTreeView, but I haven't
1914 investigated yet. */
1915 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1917 add_data_column_cell_renderer (data_sheet, column);
1922 on_variable_inserted (PsppireDict *dict, int var_index,
1923 PsppireDataSheet *data_sheet)
1925 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1926 gint base_width, incr_width;
1927 PsppSheetViewColumn *column;
1929 calc_width_conversion (data_sheet, &base_width, &incr_width);
1930 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1931 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1935 on_variable_deleted (PsppireDict *dict,
1936 const struct variable *var, int case_idx, int width,
1937 PsppireDataSheet *data_sheet)
1939 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1940 GList *columns, *iter;
1942 columns = pspp_sheet_view_get_columns (sheet_view);
1943 for (iter = columns; iter != NULL; iter = iter->next)
1945 PsppSheetViewColumn *column = iter->data;
1946 const struct variable *column_var;
1948 column_var = g_object_get_data (G_OBJECT (column), "variable");
1949 if (column_var == var)
1950 pspp_sheet_view_remove_column (sheet_view, column);
1952 g_list_free (columns);
1956 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1958 PsppireDataStore *store = data_sheet->data_store;
1963 data_sheet->data_store = NULL;
1965 g_signal_handlers_disconnect_by_func (
1966 store, G_CALLBACK (on_backend_changed), data_sheet);
1967 g_signal_handlers_disconnect_by_func (
1968 store, G_CALLBACK (on_case_inserted), data_sheet);
1969 g_signal_handlers_disconnect_by_func (
1970 store, G_CALLBACK (on_cases_deleted), data_sheet);
1971 g_signal_handlers_disconnect_by_func (
1972 store, G_CALLBACK (on_case_change), data_sheet);
1974 g_signal_handlers_disconnect_by_func (
1975 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
1976 g_signal_handlers_disconnect_by_func (
1977 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
1978 g_signal_handlers_disconnect_by_func (
1979 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
1980 g_signal_handlers_disconnect_by_func (
1981 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
1983 g_object_unref (store);
1987 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
1988 PsppireDataStore *data_store)
1990 psppire_data_sheet_unset_data_store (data_sheet);
1992 data_sheet->data_store = data_store;
1993 if (data_store != NULL)
1995 g_object_ref (data_store);
1996 g_signal_connect (data_store, "backend-changed",
1997 G_CALLBACK (on_backend_changed), data_sheet);
1998 g_signal_connect (data_store, "case-inserted",
1999 G_CALLBACK (on_case_inserted), data_sheet);
2000 g_signal_connect (data_store, "cases-deleted",
2001 G_CALLBACK (on_cases_deleted), data_sheet);
2002 g_signal_connect (data_store, "case-changed",
2003 G_CALLBACK (on_case_change), data_sheet);
2005 /* XXX it's unclean to hook into the dict this way--what if the dict
2006 changes? As of this writing, though, nothing ever changes the
2007 data_store's dict. */
2008 g_signal_connect (data_store->dict, "variable-changed",
2009 G_CALLBACK (on_variable_changed),
2011 g_signal_connect (data_store->dict, "variable-display-width-changed",
2012 G_CALLBACK (on_variable_display_width_changed),
2014 g_signal_connect (data_store->dict, "variable-inserted",
2015 G_CALLBACK (on_variable_inserted), data_sheet);
2016 g_signal_connect (data_store->dict, "variable-deleted",
2017 G_CALLBACK (on_variable_deleted), data_sheet);
2019 refresh_model (data_sheet);
2022 /* Clipboard stuff */
2024 /* A casereader and dictionary holding the data currently in the clip */
2025 static struct casereader *clip_datasheet = NULL;
2026 static struct dictionary *clip_dict = NULL;
2029 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2031 /* Set the clip from the currently selected range in DATA_SHEET. If CUT is
2032 true, clears the original data from DATA_SHEET, otherwise leaves the
2033 original data in-place. */
2035 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2038 struct casewriter *writer ;
2039 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2040 struct case_map *map = NULL;
2041 struct range_set *rows, *cols;
2042 const struct range_set_node *node;
2044 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2048 /* Destroy any existing clip */
2049 if ( clip_datasheet )
2051 casereader_destroy (clip_datasheet);
2052 clip_datasheet = NULL;
2057 dict_destroy (clip_dict);
2061 /* Construct clip dictionary. */
2062 clip_dict = dict_create (dict_get_encoding (ds->dict->dict));
2063 RANGE_SET_FOR_EACH (node, cols)
2067 for (dict_index = node->start; dict_index < node->end; dict_index++)
2069 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2070 dict_clone_var_assert (clip_dict, var);
2074 /* Construct clip data. */
2075 map = case_map_by_name (ds->dict->dict, clip_dict);
2076 writer = autopaging_writer_create (dict_get_proto (clip_dict));
2077 RANGE_SET_FOR_EACH (node, rows)
2079 unsigned long int row;
2081 for (row = node->start; row < node->end; row++)
2083 struct ccase *old = psppire_data_store_get_case (ds, row);
2085 casewriter_write (writer, case_map_execute (map, old));
2087 casewriter_force_error (writer);
2090 case_map_destroy (map);
2092 /* Clear data that we copied out, if we're doing a "cut". */
2093 if (cut && !casewriter_error (writer))
2095 RANGE_SET_FOR_EACH (node, rows)
2097 unsigned long int row;
2099 for (row = node->start; row < node->end; row++)
2101 const struct range_set_node *node2;
2103 RANGE_SET_FOR_EACH (node2, cols)
2107 for (dict_index = node2->start; dict_index < node2->end;
2110 struct variable *var;
2112 var = dict_get_var (ds->dict->dict, dict_index);
2113 psppire_data_store_set_string (ds, "", row,
2121 range_set_destroy (rows);
2122 range_set_destroy (cols);
2124 clip_datasheet = casewriter_make_reader (writer);
2126 psppire_data_sheet_update_clipboard (data_sheet);
2136 /* Perform data_out for case CC, variable V, appending to STRING */
2138 data_out_g_string (GString *string, const struct variable *v,
2139 const struct ccase *cc)
2141 const struct fmt_spec *fs = var_get_print_format (v);
2142 const union value *val = case_data (cc, v);
2144 char *s = data_out (val, var_get_encoding (v), fs);
2146 g_string_append (string, s);
2157 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2158 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2159 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2161 string = g_string_sized_new (10 * val_cnt * case_cnt);
2163 for (r = 0 ; r < case_cnt ; ++r )
2168 cc = casereader_peek (clip_datasheet, r);
2171 g_warning ("Clipboard seems to have inexplicably shrunk");
2175 for (c = 0 ; c < var_cnt ; ++c)
2177 const struct variable *v = dict_get_var (clip_dict, c);
2178 data_out_g_string (string, v, cc);
2179 if ( c < val_cnt - 1 )
2180 g_string_append (string, "\t");
2184 g_string_append (string, "\n");
2199 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2200 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2201 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2203 /* Guestimate the size needed */
2204 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2206 g_string_append (string,
2207 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2209 g_string_append (string, "<table>\n");
2210 for (r = 0 ; r < case_cnt ; ++r )
2213 struct ccase *cc = casereader_peek (clip_datasheet, r);
2216 g_warning ("Clipboard seems to have inexplicably shrunk");
2219 g_string_append (string, "<tr>\n");
2221 for (c = 0 ; c < var_cnt ; ++c)
2223 const struct variable *v = dict_get_var (clip_dict, c);
2224 g_string_append (string, "<td>");
2225 data_out_g_string (string, v, cc);
2226 g_string_append (string, "</td>\n");
2229 g_string_append (string, "</tr>\n");
2233 g_string_append (string, "</table>\n");
2241 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2242 GtkSelectionData *selection_data,
2246 GString *string = NULL;
2250 case SELECT_FMT_TEXT:
2251 string = clip_to_text ();
2253 case SELECT_FMT_HTML:
2254 string = clip_to_html ();
2257 g_assert_not_reached ();
2260 gtk_selection_data_set (selection_data, selection_data->target,
2262 (const guchar *) string->str, string->len);
2264 g_string_free (string, TRUE);
2268 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2271 dict_destroy (clip_dict);
2274 casereader_destroy (clip_datasheet);
2275 clip_datasheet = NULL;
2279 static const GtkTargetEntry targets[] = {
2280 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2281 { "STRING", 0, SELECT_FMT_TEXT },
2282 { "TEXT", 0, SELECT_FMT_TEXT },
2283 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2284 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2285 { "text/plain", 0, SELECT_FMT_TEXT },
2286 { "text/html", 0, SELECT_FMT_HTML }
2292 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2294 GtkClipboard *clipboard =
2295 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2296 GDK_SELECTION_CLIPBOARD);
2298 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2299 G_N_ELEMENTS (targets),
2300 psppire_data_sheet_clipboard_get_cb,
2301 psppire_data_sheet_clipboard_clear_cb,
2303 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2307 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2309 struct range_set *rows, *cols;
2313 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2316 range_set_destroy (rows);
2317 range_set_destroy (cols);
2320 action = get_action_assert (data_sheet->builder, "edit_copy");
2321 gtk_action_set_sensitive (action, enable);
2323 action = get_action_assert (data_sheet->builder, "edit_cut");
2324 gtk_action_set_sensitive (action, enable);
2327 /* A callback for when the clipboard contents have been received. */
2329 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2330 GtkSelectionData *sd,
2333 PsppireDataSheet *data_sheet = data;
2334 PsppireDataStore *store = data_sheet->data_store;
2335 struct range_set *rows, *cols;
2337 gint next_row, next_column;
2341 if ( sd->length < 0 )
2344 if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
2347 c = (char *) sd->data;
2349 /* Get the starting selected position in the data sheet. (Possibly we should
2350 only paste into the selected range if it's larger than one cell?) */
2351 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2353 next_row = range_set_first (rows)->start;
2354 first_column = next_column = range_set_first (cols)->start;
2355 range_set_destroy (rows);
2356 range_set_destroy (cols);
2358 g_return_if_fail (next_row >= 0);
2359 g_return_if_fail (next_column >= 0);
2361 while (count < sd->length)
2363 gint row = next_row;
2364 gint column = next_column;
2365 struct variable *var;
2368 while (*c != '\t' && *c != '\n' && count < sd->length)
2376 next_column = column + 1;
2378 else if ( *c == '\n')
2381 next_column = first_column;
2386 var = psppire_dict_get_variable (store->dict, column);
2388 psppire_data_store_set_string (store, s, row, var, FALSE);
2393 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2395 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2396 gboolean compatible_target = FALSE;
2400 for (i = 0; i < G_N_ELEMENTS (targets); i++)
2402 GdkAtom atom = gdk_atom_intern (targets[i].target, TRUE);
2403 if (gtk_clipboard_wait_is_target_available (clip, atom))
2405 compatible_target = TRUE;
2410 action = get_action_assert (data_sheet->builder, "edit_paste");
2411 gtk_action_set_sensitive (action, compatible_target);