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_clickable (column, TRUE);
229 pspp_sheet_view_column_set_cell_data_func (
230 column, renderer, render_row_number_cell, ds, NULL);
231 pspp_sheet_view_column_set_fixed_width (column, 50);
232 pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers);
233 pspp_sheet_view_append_column (sheet_view, column);
237 render_data_cell (PsppSheetViewColumn *tree_column,
238 GtkCellRenderer *cell,
241 gpointer data_sheet_)
243 PsppireDataSheet *data_sheet = data_sheet_;
244 PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet);
245 struct variable *var;
251 row = GPOINTER_TO_INT (iter->user_data);
252 var = g_object_get_data (G_OBJECT (tree_column), "variable");
254 string = psppire_data_store_get_string (store, row, var,
255 data_sheet->show_value_labels);
258 GValue gvalue = { 0 };
260 g_value_init (&gvalue, G_TYPE_STRING);
261 g_value_take_string (&gvalue, string);
262 g_object_set_property (G_OBJECT (cell), "text", &gvalue);
263 g_value_unset (&gvalue);
266 g_object_set (G_OBJECT (cell), "text", "", NULL);
268 switch (var_get_alignment (var))
270 case ALIGN_LEFT: xalign = 0.0; break;
271 case ALIGN_RIGHT: xalign = 1.0; break;
272 case ALIGN_CENTRE: xalign = 0.5; break;
273 default: xalign = 0.0; break;
282 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
286 g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
287 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
288 NULL, NULL, NULL, &width, NULL);
293 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
300 ds_put_byte_multiple (&s, '0', char_cnt);
301 ds_put_byte (&s, ' ');
302 width = get_string_width (treeview, renderer, ds_cstr (&s));
309 on_data_column_editing_started (GtkCellRenderer *cell,
310 GtkCellEditable *editable,
314 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
315 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
316 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
317 struct variable *var;
319 g_return_if_fail (column);
320 g_return_if_fail (data_sheet);
321 g_return_if_fail (data_store);
324 g_object_ref (editable);
325 g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable",
326 editable, g_object_unref);
328 var = g_object_get_data (G_OBJECT (column), "variable");
329 g_return_if_fail (var);
331 if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable))
333 const struct val_labs *labels = var_get_value_labels (var);
334 const struct val_lab *vl;
335 GtkListStore *list_store;
337 list_store = gtk_list_store_new (1, G_TYPE_STRING);
338 for (vl = val_labs_first (labels); vl != NULL;
339 vl = val_labs_next (labels, vl))
343 gtk_list_store_append (list_store, &iter);
344 gtk_list_store_set (list_store, &iter,
345 0, val_lab_get_label (vl),
349 gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
350 GTK_TREE_MODEL (list_store));
351 g_object_unref (list_store);
356 scroll_to_bottom (GtkWidget *widget,
357 GtkRequisition *requisition,
358 gpointer unused UNUSED)
360 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
361 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
362 GtkAdjustment *vadjust;
364 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
365 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
367 if (data_sheet->scroll_to_bottom_signal)
369 g_signal_handler_disconnect (data_sheet,
370 data_sheet->scroll_to_bottom_signal);
371 data_sheet->scroll_to_bottom_signal = 0;
376 on_data_column_edited (GtkCellRendererText *cell,
381 PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
382 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
383 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
384 GtkEditable *editable;
385 struct variable *var;
391 path = gtk_tree_path_new_from_string (path_string);
392 row = gtk_tree_path_get_indices (path)[0];
393 gtk_tree_path_free (path);
395 var = g_object_get_data (G_OBJECT (column), "variable");
397 new_row = row == psppire_data_store_get_case_count (data_store);
398 if (new_row && new_text[0] == '\0')
401 editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
402 g_return_if_fail (editable != NULL);
403 is_val_lab = (GTK_IS_COMBO_BOX (editable)
404 && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
405 g_object_unref (editable);
407 psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
409 if (new_row && !data_sheet->scroll_to_bottom_signal)
411 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
412 data_sheet->scroll_to_bottom_signal =
413 g_signal_connect (data_sheet, "size-request",
414 G_CALLBACK (scroll_to_bottom), NULL);
418 /* We could be more specific about what to redraw, if it seems
419 important for performance. */
420 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
425 scroll_to_right (GtkWidget *widget,
426 PsppireDataSheet *data_sheet)
428 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
429 PsppSheetViewColumn *column, *prev;
430 GList *columns, *iter;
434 columns = pspp_sheet_view_get_columns (sheet_view);
435 for (iter = columns; iter; iter = iter->next)
437 PsppSheetViewColumn *c = iter->data;
438 if (g_object_get_data (G_OBJECT (c), "new-var-column"))
445 g_list_free (columns);
450 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
456 pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
459 pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
460 gtk_tree_path_free (path);
464 if (data_sheet->scroll_to_right_signal)
466 g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
467 data_sheet->scroll_to_right_signal = 0;
472 on_new_variable_column_edited (GtkCellRendererText *cell,
477 PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
478 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
479 PsppireDict *dict = data_store->dict;
480 struct variable *var;
485 if (new_text[0] == '\0')
487 /* User didn't enter anything so don't create a variable. */
491 path = gtk_tree_path_new_from_string (path_string);
492 row = gtk_tree_path_get_indices (path)[0];
493 gtk_tree_path_free (path);
495 if (!psppire_dict_generate_name (dict, name, sizeof name))
498 var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
500 g_return_if_fail (var != NULL);
502 psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
504 if (!data_sheet->scroll_to_right_signal)
506 gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
507 data_sheet->scroll_to_right_signal =
508 g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
509 G_CALLBACK (scroll_to_right), data_sheet);
513 /* We could be more specific about what to redraw, if it seems
514 important for performance. */
515 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
520 calc_width_conversion (PsppireDataSheet *data_sheet,
521 gint *base_width, gint *incr_width)
523 GtkCellRenderer *cell;
526 cell = gtk_cell_renderer_text_new ();
527 w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
528 w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
529 *incr_width = MAX (1, (w10 - w1) / 9);
530 *base_width = MAX (0, w10 - *incr_width * 10);
531 g_object_ref_sink (cell);
532 g_object_unref (cell);
536 display_width_from_pixel_width (PsppireDataSheet *data_sheet,
539 gint base_width, incr_width;
541 calc_width_conversion (data_sheet, &base_width, &incr_width);
542 return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
546 display_width_to_pixel_width (PsppireDataSheet *data_sheet,
551 return base_width + incr_width * display_width;
555 on_data_column_resized (GObject *gobject,
559 PsppireDataSheet *data_sheet = user_data;
560 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
561 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
562 struct variable *var;
566 if (data_store == NULL)
569 pixel_width = pspp_sheet_view_column_get_width (column);
570 if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
572 /* Short-circuit the expensive display_width_from_pixel_width()
573 calculation, to make loading .sav files with 2000 columns visibly
578 var = g_object_get_data (G_OBJECT (column), "variable");
579 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
580 var_set_display_width (var, display_width);
584 do_data_column_popup_menu (PsppSheetViewColumn *column,
585 guint button, guint32 time)
587 GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
588 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
591 menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
592 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
596 on_data_column_popup_menu (PsppSheetViewColumn *column,
597 gpointer user_data UNUSED)
599 do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
603 on_column_button_press_event (PsppSheetViewColumn *column,
604 GdkEventButton *event,
605 gpointer user_data UNUSED)
607 PsppSheetSelection *selection;
608 PsppSheetView *sheet_view;
610 sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
612 g_return_val_if_fail (sheet_view != NULL, FALSE);
614 selection = pspp_sheet_view_get_selection (sheet_view);
615 g_return_val_if_fail (selection != NULL, FALSE);
617 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
619 do_data_column_popup_menu (column, event->button, event->time);
622 else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
624 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
625 struct variable *var;
627 var = g_object_get_data (G_OBJECT (column), "variable");
632 g_signal_emit_by_name (data_sheet, "var-double-clicked",
633 var_get_dict_index (var), &handled);
642 on_data_column_query_tooltip (PsppSheetViewColumn *column,
644 gpointer user_data UNUSED)
646 struct variable *var;
649 var = g_object_get_data (G_OBJECT (column), "variable");
650 g_return_val_if_fail (var != NULL, FALSE);
652 text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
653 gtk_tooltip_set_text (tooltip, text);
659 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
660 PsppSheetViewColumn *column)
662 GtkCellRenderer *cell;
663 struct variable *var;
665 var = g_object_get_data (G_OBJECT (column), "variable");
666 g_return_if_fail (var != NULL);
668 if (var_has_value_labels (var))
670 cell = gtk_cell_renderer_combo_new ();
671 g_object_set (G_OBJECT (cell),
677 cell = gtk_cell_renderer_text_new ();
679 g_signal_connect (cell, "editing-started",
680 G_CALLBACK (on_data_column_editing_started), NULL);
681 g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
683 g_object_set_data (G_OBJECT (cell), "column", column);
684 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
686 pspp_sheet_view_column_clear (column);
687 pspp_sheet_view_column_pack_start (column, cell, TRUE);
689 pspp_sheet_view_column_set_cell_data_func (
690 column, cell, render_data_cell, data_sheet, NULL);
693 static PsppSheetViewColumn *
694 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
695 gint base_width, gint incr_width)
697 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
698 struct variable *var;
699 PsppSheetViewColumn *column;
703 var = psppire_dict_get_variable (data_store->dict, dict_idx);
705 column = pspp_sheet_view_column_new ();
707 name = escape_underscores (var_get_name (var));
708 pspp_sheet_view_column_set_title (column, name);
711 g_object_set_data (G_OBJECT (column), "variable", var);
713 width = display_width_to_pixel_width (data_sheet,
714 var_get_display_width (var),
715 base_width, incr_width);
716 pspp_sheet_view_column_set_min_width (column, 10);
717 pspp_sheet_view_column_set_fixed_width (column, width);
718 pspp_sheet_view_column_set_resizable (column, TRUE);
720 pspp_sheet_view_column_set_clickable (column, TRUE);
721 g_signal_connect (column, "notify::width",
722 G_CALLBACK (on_data_column_resized), data_sheet);
724 g_signal_connect (column, "button-press-event",
725 G_CALLBACK (on_column_button_press_event),
727 g_signal_connect (column, "query-tooltip",
728 G_CALLBACK (on_data_column_query_tooltip), NULL);
729 g_signal_connect (column, "popup-menu",
730 G_CALLBACK (on_data_column_popup_menu), data_sheet);
732 add_data_column_cell_renderer (data_sheet, column);
738 make_new_variable_column (PsppireDataSheet *data_sheet,
739 gint base_width, gint incr_width)
741 PsppSheetViewColumn *column;
742 GtkCellRenderer *cell;
745 cell = gtk_cell_renderer_text_new ();
746 g_object_set (cell, "editable", TRUE, NULL);
748 g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
751 column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
752 g_object_set_data (G_OBJECT (column), "new-var-column", column);
754 width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
755 pspp_sheet_view_column_set_min_width (column, 10);
756 pspp_sheet_view_column_set_fixed_width (column, width);
758 g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
759 g_signal_connect (column, "button-press-event",
760 G_CALLBACK (on_column_button_press_event),
762 g_signal_connect (column, "popup-menu",
763 G_CALLBACK (on_data_column_popup_menu), data_sheet);
765 pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
767 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
768 data_sheet->new_variable_column = column;
772 psppire_data_sheet_model_changed (GObject *gobject,
776 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
777 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
778 PsppireDataStore *data_store;
780 /* Remove old columns. */
783 PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
787 pspp_sheet_view_remove_column (sheet_view, column);
789 data_sheet->new_variable_column = NULL;
791 if (pspp_sheet_view_get_model (sheet_view) == NULL)
793 /* Don't create any columns at all if there's no model. Otherwise we'll
794 create some columns as part of the "dispose" callback for the sheet
795 view, which sets the model to NULL. That causes warnings to be
796 logged and is obviously undesirable in any case. */
800 /* Add new columns. */
801 data_store = psppire_data_sheet_get_data_store (data_sheet);
802 if (data_store != NULL)
804 gint base_width, incr_width;
807 calc_width_conversion (data_sheet, &base_width, &incr_width);
809 make_row_number_column (data_sheet, data_store);
810 for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
812 PsppSheetViewColumn *column;
814 column = make_data_column (data_sheet, i, base_width, incr_width);
815 pspp_sheet_view_append_column (sheet_view, column);
817 make_new_variable_column (data_sheet, base_width, incr_width);
828 PROP_MAY_CREATE_VARS,
829 PROP_MAY_DELETE_VARS,
834 psppire_data_sheet_set_property (GObject *object,
839 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
843 case PROP_DATA_STORE:
844 psppire_data_sheet_set_data_store (
845 obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
848 case PROP_VALUE_LABELS:
849 psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
852 case PROP_CASE_NUMBERS:
853 psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
856 case PROP_CURRENT_CASE:
857 psppire_data_sheet_goto_case (obj, g_value_get_long (value));
860 case PROP_MAY_CREATE_VARS:
861 psppire_data_sheet_set_may_create_vars (obj,
862 g_value_get_boolean (value));
865 case PROP_MAY_DELETE_VARS:
866 psppire_data_sheet_set_may_delete_vars (obj,
867 g_value_get_boolean (value));
870 case PROP_UI_MANAGER:
872 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
878 psppire_data_sheet_get_property (GObject *object,
883 PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
887 case PROP_DATA_STORE:
888 g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
891 case PROP_VALUE_LABELS:
892 g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
895 case PROP_CASE_NUMBERS:
896 g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
899 case PROP_CURRENT_CASE:
900 g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
903 case PROP_MAY_CREATE_VARS:
904 g_value_set_boolean (value, obj->may_create_vars);
907 case PROP_MAY_DELETE_VARS:
908 g_value_set_boolean (value, obj->may_delete_vars);
911 case PROP_UI_MANAGER:
912 g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
916 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
922 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
924 return ds->show_value_labels;
928 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
929 gboolean show_value_labels)
931 show_value_labels = !!show_value_labels;
932 if (show_value_labels != ds->show_value_labels)
934 ds->show_value_labels = show_value_labels;
935 g_object_notify (G_OBJECT (ds), "value-labels");
936 gtk_widget_queue_draw (GTK_WIDGET (ds));
938 /* Make the cell being edited refresh too. */
939 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (ds), TRUE);
944 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
946 return ds->show_case_numbers;
950 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
951 gboolean show_case_numbers)
953 show_case_numbers = !!show_case_numbers;
954 if (show_case_numbers != ds->show_case_numbers)
956 PsppSheetViewColumn *column;
958 ds->show_case_numbers = show_case_numbers;
959 column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
961 pspp_sheet_view_column_set_visible (column, show_case_numbers);
963 g_object_notify (G_OBJECT (ds), "case-numbers");
964 gtk_widget_queue_draw (GTK_WIDGET (ds));
969 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
971 return data_sheet->may_create_vars;
975 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
976 gboolean may_create_vars)
978 if (data_sheet->may_create_vars != may_create_vars)
980 data_sheet->may_create_vars = may_create_vars;
981 if (data_sheet->new_variable_column)
982 pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
985 on_selection_changed (pspp_sheet_view_get_selection (
986 PSPP_SHEET_VIEW (data_sheet)), NULL);
991 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
993 return data_sheet->may_delete_vars;
997 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
998 gboolean may_delete_vars)
1000 if (data_sheet->may_delete_vars != may_delete_vars)
1002 data_sheet->may_delete_vars = may_delete_vars;
1003 on_selection_changed (pspp_sheet_view_get_selection (
1004 PSPP_SHEET_VIEW (data_sheet)), NULL);
1008 static PsppSheetViewColumn *
1009 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1012 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1013 PsppireDataStore *data_store;
1014 PsppSheetViewColumn *column;
1015 struct variable *var;
1018 data_store = psppire_data_sheet_get_data_store (data_sheet);
1019 g_return_val_if_fail (data_store != NULL, NULL);
1020 g_return_val_if_fail (data_store->dict != NULL, NULL);
1022 var = psppire_dict_get_variable (data_store->dict, dict_index);
1023 g_return_val_if_fail (var != NULL, NULL);
1026 list = pspp_sheet_view_get_columns (sheet_view);
1027 for (iter = list; iter != NULL; iter = iter->next)
1029 PsppSheetViewColumn *c = iter->data;
1032 v = g_object_get_data (G_OBJECT (c), "variable");
1045 psppire_data_sheet_show_variable (PsppireDataSheet *data_sheet,
1048 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1049 PsppSheetViewColumn *column;
1051 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1054 pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column,
1059 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1061 PsppSheetSelection *selection;
1062 struct variable *var;
1063 GList *selected_columns;
1066 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1067 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1070 for (iter = selected_columns; iter != NULL; iter = iter->next)
1072 PsppSheetViewColumn *column = iter->data;
1073 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1086 g_list_free (selected_columns);
1092 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1094 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1095 PsppireDataStore *store = data_sheet->data_store;
1096 PsppSheetSelection *selection;
1099 g_return_if_fail (case_index >= 0);
1100 g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1102 path = gtk_tree_path_new_from_indices (case_index, -1);
1104 /* Select the case. */
1105 selection = pspp_sheet_view_get_selection (sheet_view);
1106 pspp_sheet_selection_unselect_all (selection);
1107 pspp_sheet_selection_select_path (selection, path);
1108 pspp_sheet_selection_select_all_columns (selection);
1110 /* Scroll so that the case is visible. */
1111 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1113 gtk_tree_path_free (path);
1116 /* Returns the 0-based index of a selected case, if there is at least one, and
1119 If more than one case is selected, returns the one with the smallest index,
1120 that is, the index of the case closest to the beginning of the file. The
1121 row that can be used to insert a new case is not considered a case. */
1123 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1125 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1126 PsppireDataStore *store = data_sheet->data_store;
1127 const struct range_set_node *node;
1128 PsppSheetSelection *selection;
1129 struct range_set *rows;
1132 selection = pspp_sheet_view_get_selection (sheet_view);
1133 rows = pspp_sheet_selection_get_range_set (selection);
1134 node = range_set_first (rows);
1135 row = (node && node->start < psppire_data_store_get_case_count (store)
1138 range_set_destroy (rows);
1143 /* Returns the 0-based index of a selected case, if exactly one case is
1144 selected, and -1 otherwise. Returns -1 if the row that can be used to
1145 insert a new case is selected. */
1147 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1149 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1150 PsppireDataStore *store = data_sheet->data_store;
1151 const struct range_set_node *node;
1152 PsppSheetSelection *selection;
1153 struct range_set *rows;
1156 selection = pspp_sheet_view_get_selection (sheet_view);
1157 if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1160 rows = pspp_sheet_selection_get_range_set (selection);
1161 node = range_set_first (rows);
1162 row = (node && node->start < psppire_data_store_get_case_count (store)
1165 range_set_destroy (rows);
1171 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1173 if (data_sheet->uim == NULL)
1176 GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1178 GTK_TYPE_UI_MANAGER));
1179 g_object_ref (data_sheet->uim);
1182 return data_sheet->uim;
1186 psppire_data_sheet_dispose (GObject *object)
1188 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1190 if (data_sheet->dispose_has_run)
1193 data_sheet->dispose_has_run = TRUE;
1195 psppire_data_sheet_unset_data_store (data_sheet);
1197 g_object_unref (data_sheet->builder);
1199 if (data_sheet->uim)
1200 g_object_unref (data_sheet->uim);
1202 G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1206 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1208 GObjectClass *gobject_class;
1210 gobject_class = G_OBJECT_CLASS (class);
1211 gobject_class->set_property = psppire_data_sheet_set_property;
1212 gobject_class->get_property = psppire_data_sheet_get_property;
1214 gobject_class->dispose = psppire_data_sheet_dispose;
1216 g_signal_new ("var-double-clicked",
1217 G_OBJECT_CLASS_TYPE (gobject_class),
1220 g_signal_accumulator_true_handled, NULL,
1221 psppire_marshal_BOOLEAN__INT,
1222 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1224 g_object_class_install_property (
1225 gobject_class, PROP_DATA_STORE,
1226 g_param_spec_object ("data-store",
1228 "The data store for the data sheet to display.",
1229 PSPPIRE_TYPE_DATA_STORE,
1230 G_PARAM_WRITABLE | G_PARAM_READABLE));
1232 g_object_class_install_property (
1233 gobject_class, PROP_VALUE_LABELS,
1234 g_param_spec_boolean ("value-labels",
1236 "Whether or not the data sheet should display labels instead of values",
1238 G_PARAM_WRITABLE | G_PARAM_READABLE));
1240 g_object_class_install_property (
1241 gobject_class, PROP_CASE_NUMBERS,
1242 g_param_spec_boolean ("case-numbers",
1244 "Whether or not the data sheet should display case numbers",
1246 G_PARAM_WRITABLE | G_PARAM_READABLE));
1248 g_object_class_install_property (
1251 g_param_spec_long ("current-case",
1253 "Zero based number of the selected case",
1256 G_PARAM_WRITABLE | G_PARAM_READABLE));
1258 g_object_class_install_property (
1260 PROP_MAY_CREATE_VARS,
1261 g_param_spec_boolean ("may-create-vars",
1262 "May create variables",
1263 "Whether the user may create more variables",
1265 G_PARAM_READWRITE));
1267 g_object_class_install_property (
1269 PROP_MAY_DELETE_VARS,
1270 g_param_spec_boolean ("may-delete-vars",
1271 "May delete variables",
1272 "Whether the user may delete variables",
1274 G_PARAM_READWRITE));
1276 g_object_class_install_property (
1279 g_param_spec_object ("ui-manager",
1281 "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.",
1282 GTK_TYPE_UI_MANAGER,
1287 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1289 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1292 menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1293 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1297 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1299 do_popup_menu (widget, 0, gtk_get_current_event_time ());
1303 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1304 gpointer user_data UNUSED)
1306 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1308 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1310 PsppSheetSelection *selection;
1312 selection = pspp_sheet_view_get_selection (sheet_view);
1313 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1317 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1318 &path, NULL, NULL, NULL))
1320 pspp_sheet_selection_unselect_all (selection);
1321 pspp_sheet_selection_select_path (selection, path);
1322 pspp_sheet_selection_select_all_columns (selection);
1323 gtk_tree_path_free (path);
1327 do_popup_menu (widget, event->button, event->time);
1336 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1338 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1339 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1340 const struct range_set_node *node;
1341 struct range_set *selected;
1343 selected = pspp_sheet_selection_get_range_set (selection);
1344 for (node = range_set_last (selected); node != NULL;
1345 node = range_set_prev (selected, node))
1347 unsigned long int start = range_set_node_get_start (node);
1348 unsigned long int count = range_set_node_get_width (node);
1350 psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1352 range_set_destroy (selected);
1356 on_selection_changed (PsppSheetSelection *selection,
1357 gpointer user_data UNUSED)
1359 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1360 PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1361 gint n_selected_rows;
1362 gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1367 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1369 action = get_action_assert (data_sheet->builder, "edit_insert-case");
1370 gtk_action_set_sensitive (action, n_selected_rows > 0);
1372 switch (n_selected_rows)
1375 may_delete_cases = FALSE;
1379 /* The row used for inserting new cases cannot be deleted. */
1380 path = gtk_tree_path_new_from_indices (
1381 psppire_data_store_get_case_count (data_sheet->data_store), -1);
1382 may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1384 gtk_tree_path_free (path);
1388 may_delete_cases = TRUE;
1391 action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1392 gtk_action_set_sensitive (action, may_delete_cases);
1394 may_delete_vars = may_insert_vars = FALSE;
1395 list = pspp_sheet_selection_get_selected_columns (selection);
1396 for (iter = list; iter != NULL; iter = iter->next)
1398 PsppSheetViewColumn *column = iter->data;
1399 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1403 may_delete_vars = may_insert_vars = TRUE;
1406 if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1407 may_insert_vars = TRUE;
1411 may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1412 may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1414 action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1415 gtk_action_set_sensitive (action, may_insert_vars);
1417 action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1418 gtk_action_set_sensitive (action, may_delete_vars);
1420 action = get_action_assert (data_sheet->builder, "sort-up");
1421 gtk_action_set_sensitive (action, may_delete_vars);
1423 action = get_action_assert (data_sheet->builder, "sort-down");
1424 gtk_action_set_sensitive (action, may_delete_vars);
1426 psppire_data_sheet_update_clip_actions (data_sheet);
1430 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1431 struct range_set **rowsp,
1432 struct range_set **colsp)
1434 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1435 PsppireDataStore *data_store = data_sheet->data_store;
1436 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1437 unsigned long n_cases;
1438 struct range_set *rows, *cols;
1441 if (data_store == NULL)
1443 n_cases = psppire_data_store_get_case_count (data_store);
1445 rows = pspp_sheet_selection_get_range_set (selection);
1446 range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1447 if (range_set_is_empty (rows))
1449 range_set_destroy (rows);
1453 cols = range_set_create ();
1454 list = pspp_sheet_selection_get_selected_columns (selection);
1455 for (iter = list; iter != NULL; iter = iter->next)
1457 PsppSheetViewColumn *column = iter->data;
1458 struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1461 range_set_set1 (cols, var_get_dict_index (var), 1);
1464 if (range_set_is_empty (cols))
1466 range_set_destroy (rows);
1467 range_set_destroy (cols);
1477 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1479 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1480 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1481 PsppireDataStore *data_store = data_sheet->data_store;
1482 struct range_set *selected;
1485 selected = pspp_sheet_selection_get_range_set (selection);
1486 row = range_set_scan (selected, 0);
1487 range_set_destroy (selected);
1489 if (row <= psppire_data_store_get_case_count (data_store))
1490 psppire_data_store_insert_new_case (data_store, row);
1494 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1496 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1497 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1498 PsppireDict *dict = data_sheet->data_store->dict;
1499 PsppSheetViewColumn *column;
1500 struct variable *var;
1505 list = pspp_sheet_selection_get_selected_columns (selection);
1508 column = list->data;
1511 var = g_object_get_data (G_OBJECT (column), "variable");
1512 index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1513 if (psppire_dict_generate_name (dict, name, sizeof name))
1514 psppire_dict_insert_variable (dict, index, name);
1518 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1520 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1521 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1522 PsppireDict *dict = data_sheet->data_store->dict;
1525 list = pspp_sheet_selection_get_selected_columns (selection);
1528 list = g_list_reverse (list);
1529 for (iter = list; iter; iter = iter->next)
1531 PsppSheetViewColumn *column = iter->data;
1532 struct variable *var;
1534 var = g_object_get_data (G_OBJECT (column), "variable");
1536 psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1548 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1550 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1551 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1552 PsppireDataWindow *pdw;
1557 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1558 g_return_if_fail (pdw != NULL);
1560 list = pspp_sheet_selection_get_selected_columns (selection);
1562 syntax = g_string_new ("SORT CASES BY");
1564 for (iter = list; iter; iter = iter->next)
1566 PsppSheetViewColumn *column = iter->data;
1567 struct variable *var;
1569 var = g_object_get_data (G_OBJECT (column), "variable");
1572 g_string_append_printf (syntax, " %s", var_get_name (var));
1578 if (order == SORT_DESCEND)
1579 g_string_append (syntax, " (DOWN)");
1580 g_string_append_c (syntax, '.');
1581 execute_const_syntax_string (pdw, syntax->str);
1583 g_string_free (syntax, TRUE);
1587 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1589 do_sort (data_sheet, SORT_ASCEND);
1593 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1595 do_sort (data_sheet, SORT_DESCEND);
1599 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1601 goto_case_dialog (data_sheet);
1605 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1607 PsppireDataWindow *pdw;
1609 pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1610 g_return_if_fail (pdw != NULL);
1616 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1618 psppire_data_sheet_set_clip (data_sheet);
1622 psppire_data_sheet_init (PsppireDataSheet *obj)
1624 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1627 obj->show_value_labels = FALSE;
1628 obj->show_case_numbers = TRUE;
1629 obj->may_create_vars = TRUE;
1630 obj->may_delete_vars = TRUE;
1632 obj->scroll_to_bottom_signal = 0;
1633 obj->scroll_to_right_signal = 0;
1634 obj->new_variable_column = NULL;
1635 obj->container = NULL;
1638 obj->dispose_has_run = FALSE;
1640 pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1642 g_signal_connect (obj, "notify::model",
1643 G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1645 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1646 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1647 PSPP_SHEET_SELECTION_RECTANGLE);
1649 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1650 g_signal_connect (obj, "query-tooltip",
1651 G_CALLBACK (on_query_tooltip), NULL);
1652 g_signal_connect (obj, "button-press-event",
1653 G_CALLBACK (on_button_pressed), NULL);
1654 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1656 obj->builder = builder_new ("data-sheet.ui");
1658 action = get_action_assert (obj->builder, "edit_clear-cases");
1659 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1661 gtk_action_set_sensitive (action, FALSE);
1662 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1663 "changed", G_CALLBACK (on_selection_changed), NULL);
1665 action = get_action_assert (obj->builder, "edit_insert-case");
1666 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1669 action = get_action_assert (obj->builder, "edit_insert-variable");
1670 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1673 action = get_action_assert (obj->builder, "edit_goto-case");
1674 g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1677 action = get_action_assert (obj->builder, "edit_copy");
1678 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1680 action = get_action_assert (obj->builder, "edit_clear-variables");
1681 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1684 action = get_action_assert (obj->builder, "edit_find");
1685 g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1687 action = get_action_assert (obj->builder, "sort-up");
1688 g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1690 action = get_action_assert (obj->builder, "sort-down");
1691 g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1696 psppire_data_sheet_new (void)
1698 return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1702 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1704 return data_sheet->data_store;
1708 refresh_model (PsppireDataSheet *data_sheet)
1710 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1712 if (data_sheet->data_store != NULL)
1714 PsppireEmptyListStore *model;
1718 n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1719 model = psppire_empty_list_store_new (n_rows);
1720 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1721 GTK_TREE_MODEL (model));
1722 g_object_unref (model);
1724 action = get_action_assert (data_sheet->builder, "edit_copy");
1725 g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1731 on_case_inserted (PsppireDataStore *data_store, gint row,
1732 PsppireDataSheet *data_sheet)
1734 PsppireEmptyListStore *empty_list_store;
1735 GtkTreeModel *tree_model;
1738 g_return_if_fail (data_store == data_sheet->data_store);
1740 n_rows = psppire_data_store_get_case_count (data_store) + 1;
1741 if (row == n_rows - 1)
1744 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1745 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1746 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1747 psppire_empty_list_store_row_inserted (empty_list_store, row);
1751 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1752 PsppireDataSheet *data_sheet)
1755 g_return_if_fail (data_store == data_sheet->data_store);
1759 /* This is a bit of a cop-out. We could do better, if it ever turns out
1760 that this performs too poorly. */
1761 refresh_model (data_sheet);
1765 PsppireEmptyListStore *empty_list_store;
1766 GtkTreeModel *tree_model;
1767 gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1769 tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1770 empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1771 psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1772 psppire_empty_list_store_row_deleted (empty_list_store, first);
1777 on_case_change (PsppireDataStore *data_store, gint row,
1778 PsppireDataSheet *data_sheet)
1780 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1782 pspp_sheet_view_stop_editing (sheet_view, TRUE);
1783 gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1787 on_backend_changed (PsppireDataStore *data_store,
1788 PsppireDataSheet *data_sheet)
1790 g_return_if_fail (data_store == data_sheet->data_store);
1791 refresh_model (data_sheet);
1795 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1796 PsppireDataSheet *data_sheet)
1798 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1799 PsppSheetViewColumn *column;
1800 struct variable *var;
1804 g_return_if_fail (data_sheet->data_store != NULL);
1805 g_return_if_fail (dict == data_sheet->data_store->dict);
1807 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1812 var = psppire_dict_get_variable (data_store->dict, dict_index);
1813 g_return_if_fail (var != NULL);
1815 pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1816 display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1817 if (display_width != var_get_display_width (var))
1819 gint base_width, incr_width;
1821 display_width = var_get_display_width (var);
1822 calc_width_conversion (data_sheet, &base_width, &incr_width);
1823 pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1824 base_width, incr_width);
1825 pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1830 on_variable_changed (PsppireDict *dict, int dict_index,
1831 PsppireDataSheet *data_sheet)
1833 PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1834 PsppSheetViewColumn *column;
1835 GtkCellRenderer *cell;
1836 struct variable *var;
1840 g_return_if_fail (data_sheet->data_store != NULL);
1841 g_return_if_fail (dict == data_sheet->data_store->dict);
1843 column = psppire_data_sheet_find_column_for_variable (data_sheet,
1848 var = psppire_dict_get_variable (data_store->dict, dict_index);
1849 g_return_if_fail (var != NULL);
1851 name = escape_underscores (var_get_name (var));
1852 if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1853 pspp_sheet_view_column_set_title (column, name);
1856 cells = pspp_sheet_view_column_get_cell_renderers (column);
1857 g_return_if_fail (cells);
1859 g_list_free (cells);
1861 if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1863 /* Stop editing before we delete and replace the cell renderers.
1864 Otherwise if this column is currently being edited, an eventual call
1865 to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1866 that to gtk_cell_renderer_stop_editing(), which causes a critical.
1868 It's possible that this is a bug in PsppSheetView, and it's possible
1869 that PsppSheetView inherits that from GtkTreeView, but I haven't
1870 investigated yet. */
1871 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1873 add_data_column_cell_renderer (data_sheet, column);
1878 on_variable_inserted (PsppireDict *dict, int var_index,
1879 PsppireDataSheet *data_sheet)
1881 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1882 gint base_width, incr_width;
1883 PsppSheetViewColumn *column;
1885 calc_width_conversion (data_sheet, &base_width, &incr_width);
1886 column = make_data_column (data_sheet, var_index, base_width, incr_width);
1887 pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1891 on_variable_deleted (PsppireDict *dict,
1892 const struct variable *var, int case_idx, int width,
1893 PsppireDataSheet *data_sheet)
1895 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1896 GList *columns, *iter;
1898 columns = pspp_sheet_view_get_columns (sheet_view);
1899 for (iter = columns; iter != NULL; iter = iter->next)
1901 PsppSheetViewColumn *column = iter->data;
1902 const struct variable *column_var;
1904 column_var = g_object_get_data (G_OBJECT (column), "variable");
1905 if (column_var == var)
1906 pspp_sheet_view_remove_column (sheet_view, column);
1908 g_list_free (columns);
1912 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1914 PsppireDataStore *store = data_sheet->data_store;
1919 data_sheet->data_store = NULL;
1921 g_signal_handlers_disconnect_by_func (
1922 store, G_CALLBACK (on_backend_changed), data_sheet);
1923 g_signal_handlers_disconnect_by_func (
1924 store, G_CALLBACK (on_case_inserted), data_sheet);
1925 g_signal_handlers_disconnect_by_func (
1926 store, G_CALLBACK (on_cases_deleted), data_sheet);
1927 g_signal_handlers_disconnect_by_func (
1928 store, G_CALLBACK (on_case_change), data_sheet);
1930 g_signal_handlers_disconnect_by_func (
1931 store->dict, G_CALLBACK (on_variable_changed), data_sheet);
1932 g_signal_handlers_disconnect_by_func (
1933 store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
1934 g_signal_handlers_disconnect_by_func (
1935 store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
1936 g_signal_handlers_disconnect_by_func (
1937 store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
1939 g_object_unref (store);
1943 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
1944 PsppireDataStore *data_store)
1946 psppire_data_sheet_unset_data_store (data_sheet);
1948 data_sheet->data_store = data_store;
1949 if (data_store != NULL)
1951 g_object_ref (data_store);
1952 g_signal_connect (data_store, "backend-changed",
1953 G_CALLBACK (on_backend_changed), data_sheet);
1954 g_signal_connect (data_store, "case-inserted",
1955 G_CALLBACK (on_case_inserted), data_sheet);
1956 g_signal_connect (data_store, "cases-deleted",
1957 G_CALLBACK (on_cases_deleted), data_sheet);
1958 g_signal_connect (data_store, "case-changed",
1959 G_CALLBACK (on_case_change), data_sheet);
1961 /* XXX it's unclean to hook into the dict this way--what if the dict
1962 changes? As of this writing, though, nothing ever changes the
1963 data_store's dict. */
1964 g_signal_connect (data_store->dict, "variable-changed",
1965 G_CALLBACK (on_variable_changed),
1967 g_signal_connect (data_store->dict, "variable-display-width-changed",
1968 G_CALLBACK (on_variable_display_width_changed),
1970 g_signal_connect (data_store->dict, "variable-inserted",
1971 G_CALLBACK (on_variable_inserted), data_sheet);
1972 g_signal_connect (data_store->dict, "variable-deleted",
1973 G_CALLBACK (on_variable_deleted), data_sheet);
1975 refresh_model (data_sheet);
1978 /* Clipboard stuff */
1980 /* A casereader and dictionary holding the data currently in the clip */
1981 static struct casereader *clip_datasheet = NULL;
1982 static struct dictionary *clip_dict = NULL;
1985 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
1987 /* Set the clip according to the currently
1988 selected range in the data sheet */
1990 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet)
1992 struct casewriter *writer ;
1993 PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
1994 struct case_map *map = NULL;
1995 struct range_set *rows, *cols;
1996 const struct range_set_node *node;
1998 if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2002 /* Destroy any existing clip */
2003 if ( clip_datasheet )
2005 casereader_destroy (clip_datasheet);
2006 clip_datasheet = NULL;
2011 dict_destroy (clip_dict);
2015 /* Construct clip dictionary. */
2016 clip_dict = dict_create (dict_get_encoding (ds->dict->dict));
2017 RANGE_SET_FOR_EACH (node, cols)
2021 for (dict_index = node->start; dict_index < node->end; dict_index++)
2023 struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2024 dict_clone_var_assert (clip_dict, var);
2028 /* Construct clip data. */
2029 map = case_map_by_name (ds->dict->dict, clip_dict);
2030 writer = autopaging_writer_create (dict_get_proto (clip_dict));
2031 RANGE_SET_FOR_EACH (node, rows)
2033 unsigned long int row;
2035 for (row = node->start; row < node->end; row++)
2037 struct ccase *old = psppire_data_store_get_case (ds, row);
2039 casewriter_write (writer, case_map_execute (map, old));
2041 casewriter_force_error (writer);
2044 case_map_destroy (map);
2046 range_set_destroy (rows);
2047 range_set_destroy (cols);
2049 clip_datasheet = casewriter_make_reader (writer);
2051 psppire_data_sheet_update_clipboard (data_sheet);
2061 /* Perform data_out for case CC, variable V, appending to STRING */
2063 data_out_g_string (GString *string, const struct variable *v,
2064 const struct ccase *cc)
2066 const struct fmt_spec *fs = var_get_print_format (v);
2067 const union value *val = case_data (cc, v);
2069 char *s = data_out (val, var_get_encoding (v), fs);
2071 g_string_append (string, s);
2082 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2083 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2084 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2086 string = g_string_sized_new (10 * val_cnt * case_cnt);
2088 for (r = 0 ; r < case_cnt ; ++r )
2093 cc = casereader_peek (clip_datasheet, r);
2096 g_warning ("Clipboard seems to have inexplicably shrunk");
2100 for (c = 0 ; c < var_cnt ; ++c)
2102 const struct variable *v = dict_get_var (clip_dict, c);
2103 data_out_g_string (string, v, cc);
2104 if ( c < val_cnt - 1 )
2105 g_string_append (string, "\t");
2109 g_string_append (string, "\n");
2124 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2125 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2126 const size_t var_cnt = dict_get_var_cnt (clip_dict);
2128 /* Guestimate the size needed */
2129 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2131 g_string_append (string,
2132 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2134 g_string_append (string, "<table>\n");
2135 for (r = 0 ; r < case_cnt ; ++r )
2138 struct ccase *cc = casereader_peek (clip_datasheet, r);
2141 g_warning ("Clipboard seems to have inexplicably shrunk");
2144 g_string_append (string, "<tr>\n");
2146 for (c = 0 ; c < var_cnt ; ++c)
2148 const struct variable *v = dict_get_var (clip_dict, c);
2149 g_string_append (string, "<td>");
2150 data_out_g_string (string, v, cc);
2151 g_string_append (string, "</td>\n");
2154 g_string_append (string, "</tr>\n");
2158 g_string_append (string, "</table>\n");
2166 psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
2167 GtkSelectionData *selection_data,
2171 GString *string = NULL;
2175 case SELECT_FMT_TEXT:
2176 string = clip_to_text ();
2178 case SELECT_FMT_HTML:
2179 string = clip_to_html ();
2182 g_assert_not_reached ();
2185 gtk_selection_data_set (selection_data, selection_data->target,
2187 (const guchar *) string->str, string->len);
2189 g_string_free (string, TRUE);
2193 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2196 dict_destroy (clip_dict);
2199 casereader_destroy (clip_datasheet);
2200 clip_datasheet = NULL;
2204 static const GtkTargetEntry targets[] = {
2205 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
2206 { "STRING", 0, SELECT_FMT_TEXT },
2207 { "TEXT", 0, SELECT_FMT_TEXT },
2208 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2209 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2210 { "text/plain", 0, SELECT_FMT_TEXT },
2211 { "text/html", 0, SELECT_FMT_HTML }
2217 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2219 GtkClipboard *clipboard =
2220 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2221 GDK_SELECTION_CLIPBOARD);
2223 if (!gtk_clipboard_set_with_owner (clipboard, targets,
2224 G_N_ELEMENTS (targets),
2225 psppire_data_sheet_clipboard_get_cb,
2226 psppire_data_sheet_clipboard_clear_cb,
2228 psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2232 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2234 struct range_set *rows, *cols;
2238 enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2241 range_set_destroy (rows);
2242 range_set_destroy (cols);
2245 action = get_action_assert (data_sheet->builder, "edit_copy");
2246 gtk_action_set_sensitive (action, enable);
2248 action = get_action_assert (data_sheet->builder, "edit_cut");
2249 gtk_action_set_sensitive (action, enable);