gui: Make Tab, Shift+Tab skip row head and new-var columns in data sheet.
[pspp] / src / ui / gui / psppire-data-sheet.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2011, 2012 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "ui/gui/psppire-data-sheet.h"
20
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"
42
43 #include "gl/intprops.h"
44 #include "gl/xalloc.h"
45
46 #include <gettext.h>
47 #define _(msgid) gettext (msgid)
48 #define N_(msgid) msgid
49
50 static void psppire_data_sheet_dispose (GObject *);
51 static void psppire_data_sheet_unset_data_store (PsppireDataSheet *);
52
53 static void psppire_data_sheet_update_clip_actions (PsppireDataSheet *);
54 static void psppire_data_sheet_set_clip (PsppireDataSheet *, gboolean cut);
55
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);
60
61 G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, PSPP_TYPE_SHEET_VIEW);
62
63 static gboolean
64 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
65                       gint wx, gint wy,
66                       size_t *row, PsppSheetViewColumn **columnp)
67 {
68   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
69   gint bx, by;
70   GtkTreePath *path;
71   GtkTreeIter iter;
72   PsppSheetViewColumn *tree_column;
73   GtkTreeModel *tree_model;
74   bool ok;
75
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))
87     return FALSE;
88
89   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
90                                                      wx, wy, &bx, &by);
91   if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
92                                       &path, &tree_column, NULL, NULL))
93     return FALSE;
94
95   *columnp = tree_column;
96
97   pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
98                                     NULL);
99
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);
103   if (!ok)
104     return FALSE;
105
106   *row = GPOINTER_TO_INT (iter.user_data);
107   return TRUE;
108 }
109
110 static gboolean
111 on_query_tooltip (GtkWidget *widget, gint wx, gint wy,
112                   gboolean keyboard_mode UNUSED,
113                   GtkTooltip *tooltip, gpointer data UNUSED)
114 {
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;
119   const char *label;
120   union value v;
121   size_t row;
122   int width;
123
124   g_return_val_if_fail (data_store != NULL, FALSE);
125
126   if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
127     return FALSE;
128
129   var = g_object_get_data (G_OBJECT (column), "variable");
130   if (var == NULL)
131     {
132       if (g_object_get_data (G_OBJECT (column), "new-var-column") == NULL)
133         return FALSE;
134
135       gtk_tooltip_set_text (tooltip,
136                             _("Enter a number to add a new variable."));
137       return TRUE;
138     }
139   else if (row >= datasheet_get_n_rows (data_store->datasheet))
140     {
141       gtk_tooltip_set_text (tooltip, _("Enter a number to add a new case."));
142       return TRUE;
143     }
144
145   width = var_get_width (var);
146
147   value_init (&v, width);
148   datasheet_get_value (data_store->datasheet, row, var_get_case_index (var),
149                        &v);
150
151   label = var_lookup_value_label (var, &v);
152   if (label != NULL)
153     {
154       if (data_sheet->show_value_labels)
155         {
156           char *s = value_to_text (v, var);
157           gtk_tooltip_set_text (tooltip, s);
158           free (s);
159         }
160       else
161         gtk_tooltip_set_text (tooltip, label);
162     }
163   value_destroy (&v, width);
164
165   return label != NULL;
166 }
167
168 static void
169 render_row_number_cell (PsppSheetViewColumn *tree_column,
170                         GtkCellRenderer *cell,
171                         GtkTreeModel *model,
172                         GtkTreeIter *iter,
173                         gpointer store_)
174 {
175   PsppireDataStore *store = store_;
176   GValue gvalue = { 0, };
177   gint row;
178
179   row = GPOINTER_TO_INT (iter->user_data);
180
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);
185
186   if (row < datasheet_get_n_rows (store->datasheet))
187     g_object_set (cell, "editable", TRUE, NULL);
188   else
189     g_object_set (cell, "editable", FALSE, NULL);
190
191   g_object_set (cell,
192                 "slash", psppire_data_store_filtered (store, row),
193                 NULL);
194 }
195
196 static void
197 on_row_number_clicked (PsppireCellRendererButton *button,
198                        gchar *path_string,
199                        PsppSheetView *sheet_view)
200 {
201   PsppSheetSelection *selection;
202   GtkTreePath *path;
203
204   path = gtk_tree_path_new_from_string (path_string);
205
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);
210
211   gtk_tree_path_free (path);
212 }
213
214 static void
215 make_row_number_column (PsppireDataSheet *data_sheet,
216                         PsppireDataStore *ds)
217 {
218   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
219   PsppSheetViewColumn *column;
220   GtkCellRenderer *renderer;
221
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),
225                     sheet_view);
226
227   column = pspp_sheet_view_column_new_with_attributes (_("Case"),
228                                                        renderer, NULL);
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_tabbable (column, FALSE);
232   pspp_sheet_view_column_set_clickable (column, TRUE);
233   pspp_sheet_view_column_set_cell_data_func (
234     column, renderer, render_row_number_cell, ds, NULL);
235   pspp_sheet_view_column_set_fixed_width (column, 50);
236   pspp_sheet_view_column_set_visible (column, data_sheet->show_case_numbers);
237   pspp_sheet_view_append_column (sheet_view, column);
238 }
239
240 static void
241 render_data_cell (PsppSheetViewColumn *tree_column,
242                   GtkCellRenderer *cell,
243                   GtkTreeModel *model,
244                   GtkTreeIter *iter,
245                   gpointer data_sheet_)
246 {
247   PsppireDataSheet *data_sheet = data_sheet_;
248   PsppireDataStore *store = psppire_data_sheet_get_data_store (data_sheet);
249   struct variable *var;
250   gchar *string;
251   gint row;
252
253   double xalign;
254
255   row = GPOINTER_TO_INT (iter->user_data);
256   var = g_object_get_data (G_OBJECT (tree_column), "variable");
257
258   string = psppire_data_store_get_string (store, row, var,
259                                           data_sheet->show_value_labels);
260   if (string != NULL)
261     {
262       GValue gvalue = { 0 };
263
264       g_value_init (&gvalue, G_TYPE_STRING);
265       g_value_take_string (&gvalue, string);
266       g_object_set_property (G_OBJECT (cell), "text", &gvalue);
267       g_value_unset (&gvalue);
268     }
269   else
270     g_object_set (G_OBJECT (cell), "text", "", NULL);
271
272   switch (var_get_alignment (var))
273     {
274     case ALIGN_LEFT: xalign = 0.0; break;
275     case ALIGN_RIGHT: xalign = 1.0; break;
276     case ALIGN_CENTRE: xalign = 0.5; break;
277     default: xalign = 0.0; break;
278     }
279   g_object_set (cell,
280                 "xalign", xalign,
281                 "editable", TRUE,
282                 NULL);
283 }
284
285 static gint
286 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
287                   const char *string)
288 {
289   gint width;
290   g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL);
291   gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
292                               NULL, NULL, NULL, &width, NULL);
293   return width;
294 }
295
296 static gint
297 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
298                      size_t char_cnt)
299 {
300   struct string s;
301   gint width;
302
303   ds_init_empty (&s);
304   ds_put_byte_multiple (&s, '0', char_cnt);
305   ds_put_byte (&s, ' ');
306   width = get_string_width (treeview, renderer, ds_cstr (&s));
307   ds_destroy (&s);
308
309   return width;
310 }
311
312 static void
313 on_data_column_editing_started (GtkCellRenderer *cell,
314                                 GtkCellEditable *editable,
315                                 const gchar     *path,
316                                 gpointer         user_data)
317 {
318   PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
319   PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
320   PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
321   struct variable *var;
322
323   g_return_if_fail (column);
324   g_return_if_fail (data_sheet);
325   g_return_if_fail (data_store);
326
327
328   g_object_ref (editable);
329   g_object_set_data_full (G_OBJECT (cell), "data-sheet-editable",
330                           editable, g_object_unref);
331
332   var = g_object_get_data (G_OBJECT (column), "variable");
333   g_return_if_fail (var);
334
335   if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable))
336     {
337       const struct val_labs *labels = var_get_value_labels (var);
338       const struct val_lab *vl;
339       GtkListStore *list_store;
340
341       list_store = gtk_list_store_new (1, G_TYPE_STRING);
342       for (vl = val_labs_first (labels); vl != NULL;
343            vl = val_labs_next (labels, vl))
344         {
345           GtkTreeIter iter;
346
347           gtk_list_store_append (list_store, &iter);
348           gtk_list_store_set (list_store, &iter,
349                               0, val_lab_get_label (vl),
350                               -1);
351         }
352
353       gtk_combo_box_set_model (GTK_COMBO_BOX (editable),
354                                GTK_TREE_MODEL (list_store));
355       g_object_unref (list_store);
356     }
357 }
358
359 static void
360 scroll_to_bottom (GtkWidget      *widget,
361                   GtkRequisition *requisition,
362                   gpointer        unused UNUSED)
363 {
364   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
365   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
366   GtkAdjustment *vadjust;
367
368   vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
369   gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
370
371   if (data_sheet->scroll_to_bottom_signal)
372     {
373       g_signal_handler_disconnect (data_sheet,
374                                    data_sheet->scroll_to_bottom_signal);
375       data_sheet->scroll_to_bottom_signal = 0;
376     }
377 }
378
379 static void
380 on_data_column_edited (GtkCellRendererText *cell,
381                        gchar               *path_string,
382                        gchar               *new_text,
383                        gpointer             user_data)
384 {
385   PsppSheetViewColumn *column = g_object_get_data (G_OBJECT (cell), "column");
386   PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
387   PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
388   GtkEditable *editable;
389   struct variable *var;
390   GtkTreePath *path;
391   gboolean is_val_lab;
392   gboolean new_row;
393   gint row;
394
395   path = gtk_tree_path_new_from_string (path_string);
396   row = gtk_tree_path_get_indices (path)[0];
397   gtk_tree_path_free (path);
398
399   var = g_object_get_data (G_OBJECT (column), "variable");
400
401   new_row = row == psppire_data_store_get_case_count (data_store);
402   if (new_row && new_text[0] == '\0')
403     return;
404
405   editable = g_object_steal_data (G_OBJECT (cell), "data-sheet-editable");
406   g_return_if_fail (editable != NULL);
407   is_val_lab = (GTK_IS_COMBO_BOX (editable)
408                 && gtk_combo_box_get_active (GTK_COMBO_BOX (editable)) >= 0);
409   g_object_unref (editable);
410
411   psppire_data_store_set_string (data_store, new_text, row, var, is_val_lab);
412
413   if (new_row && !data_sheet->scroll_to_bottom_signal)
414     {
415       gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
416       data_sheet->scroll_to_bottom_signal =
417         g_signal_connect (data_sheet, "size-request",
418                           G_CALLBACK (scroll_to_bottom), NULL);
419     }
420   else
421     {
422       /* We could be more specific about what to redraw, if it seems
423          important for performance. */
424       gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
425     }
426 }
427
428 static void
429 scroll_to_right (GtkWidget      *widget,
430                  PsppireDataSheet  *data_sheet)
431 {
432   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
433   PsppSheetViewColumn *column, *prev;
434   GList *columns, *iter;
435
436   column = NULL;
437   prev = NULL;
438   columns = pspp_sheet_view_get_columns (sheet_view);
439   for (iter = columns; iter; iter = iter->next)
440     {
441       PsppSheetViewColumn *c = iter->data;
442       if (g_object_get_data (G_OBJECT (c), "new-var-column"))
443         {
444           column = c;
445           break;
446         }
447       prev = c;
448     }
449   g_list_free (columns);
450
451   if (column == NULL)
452     return;
453
454   pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, FALSE, 0, 0);
455
456   if (prev)
457     {
458       GtkTreePath *path;
459
460       pspp_sheet_view_get_cursor (sheet_view, &path, NULL);
461       if (path)
462         {
463           pspp_sheet_view_set_cursor (sheet_view, path, prev, TRUE);
464           gtk_tree_path_free (path);
465         }
466     }
467
468   if (data_sheet->scroll_to_right_signal)
469     {
470       g_signal_handler_disconnect (widget, data_sheet->scroll_to_right_signal);
471       data_sheet->scroll_to_right_signal = 0;
472     }
473 }
474
475 static void
476 on_new_variable_column_edited (GtkCellRendererText *cell,
477                                gchar               *path_string,
478                                gchar               *new_text,
479                                gpointer             user_data)
480 {
481   PsppireDataSheet *data_sheet = g_object_get_data (G_OBJECT (cell), "data-sheet");
482   PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
483   PsppireDict *dict = data_store->dict;
484   struct variable *var;
485   GtkTreePath *path;
486   char name[64];
487   gint row;
488
489   if (new_text[0] == '\0')
490     {
491       /* User didn't enter anything so don't create a variable. */
492       return;
493     }
494
495   path = gtk_tree_path_new_from_string (path_string);
496   row = gtk_tree_path_get_indices (path)[0];
497   gtk_tree_path_free (path);
498
499   if (!psppire_dict_generate_name (dict, name, sizeof name))
500     return;
501
502   var = psppire_dict_insert_variable (dict, psppire_dict_get_var_cnt (dict),
503                                       name);
504   g_return_if_fail (var != NULL);
505
506   psppire_data_store_set_string (data_store, new_text, row, var, FALSE);
507
508   if (!data_sheet->scroll_to_right_signal)
509     {
510       gtk_widget_queue_resize (GTK_WIDGET (data_sheet));
511       data_sheet->scroll_to_right_signal =
512         g_signal_connect_after (gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)), "check-resize",
513                                 G_CALLBACK (scroll_to_right), data_sheet);
514     }
515   else
516     {
517       /* We could be more specific about what to redraw, if it seems
518          important for performance. */
519       gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
520     }
521 }
522
523 static void
524 calc_width_conversion (PsppireDataSheet *data_sheet,
525                        gint *base_width, gint *incr_width)
526 {
527   GtkCellRenderer *cell;
528   gint w1, w10;
529
530   cell = gtk_cell_renderer_text_new ();
531   w1 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 1);
532   w10 = get_monospace_width (PSPP_SHEET_VIEW (data_sheet), cell, 10);
533   *incr_width = MAX (1, (w10 - w1) / 9);
534   *base_width = MAX (0, w10 - *incr_width * 10);
535   g_object_ref_sink (cell);
536   g_object_unref (cell);
537 }
538
539 static gint
540 display_width_from_pixel_width (PsppireDataSheet *data_sheet,
541                                 gint pixel_width)
542 {
543   gint base_width, incr_width;
544
545   calc_width_conversion (data_sheet, &base_width, &incr_width);
546   return MAX ((pixel_width - base_width + incr_width / 2) / incr_width, 1);
547 }
548
549 static gint
550 display_width_to_pixel_width (PsppireDataSheet *data_sheet,
551                               gint display_width,
552                               gint base_width,
553                               gint incr_width)
554 {
555   return base_width + incr_width * display_width;
556 }
557
558 static void
559 on_data_column_resized (GObject    *gobject,
560                         GParamSpec *pspec,
561                         gpointer    user_data)
562 {
563   PsppireDataSheet *data_sheet = user_data;
564   PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
565   PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (gobject);
566   struct variable *var;
567   gint pixel_width;
568   int display_width;
569
570   if (data_store == NULL)
571     return;
572
573   pixel_width = pspp_sheet_view_column_get_width (column);
574   if (pixel_width == pspp_sheet_view_column_get_fixed_width (column))
575     {
576       /* Short-circuit the expensive display_width_from_pixel_width()
577          calculation, to make loading .sav files with 2000 columns visibly
578          faster. */
579       return;
580     }
581
582   var = g_object_get_data (G_OBJECT (column), "variable");
583   display_width = display_width_from_pixel_width (data_sheet, pixel_width);
584   var_set_display_width (var, display_width);
585 }
586
587 static void
588 do_data_column_popup_menu (PsppSheetViewColumn *column,
589                            guint button, guint32 time)
590 {
591   GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column);
592   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
593   GtkWidget *menu;
594
595   menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup");
596   gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
597 }
598
599 static void
600 on_data_column_popup_menu (PsppSheetViewColumn *column,
601                            gpointer user_data UNUSED)
602 {
603   do_data_column_popup_menu (column, 0, gtk_get_current_event_time ());
604 }
605
606 static gboolean
607 on_column_button_press_event (PsppSheetViewColumn *column,
608                               GdkEventButton *event,
609                               gpointer user_data UNUSED)
610 {
611   PsppSheetSelection *selection;
612   PsppSheetView *sheet_view;
613
614   sheet_view = PSPP_SHEET_VIEW (pspp_sheet_view_column_get_tree_view (
615                                   column));
616   g_return_val_if_fail (sheet_view != NULL, FALSE);
617
618   selection = pspp_sheet_view_get_selection (sheet_view);
619   g_return_val_if_fail (selection != NULL, FALSE);
620
621   if (event->type == GDK_BUTTON_PRESS && event->button == 3)
622     {
623       do_data_column_popup_menu (column, event->button, event->time);
624       return TRUE;
625     }
626   else if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
627     {
628       PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
629       struct variable *var;
630
631       var = g_object_get_data (G_OBJECT (column), "variable");
632       if (var != NULL)
633         {
634           gboolean handled;
635
636           g_signal_emit_by_name (data_sheet, "var-double-clicked",
637                                  var_get_dict_index (var), &handled);
638           return handled;
639         }
640     }
641
642   return FALSE;
643 }
644
645 static gboolean
646 on_data_column_query_tooltip (PsppSheetViewColumn *column,
647                               GtkTooltip *tooltip,
648                               gpointer user_data UNUSED)
649 {
650   struct variable *var;
651   const char *text;
652
653   var = g_object_get_data (G_OBJECT (column), "variable");
654   g_return_val_if_fail (var != NULL, FALSE);
655
656   text = var_has_label (var) ? var_get_label (var) : var_get_name (var);
657   gtk_tooltip_set_text (tooltip, text);
658
659   return TRUE;
660 }
661
662 static void
663 add_data_column_cell_renderer (PsppireDataSheet *data_sheet,
664                                PsppSheetViewColumn *column)
665 {
666   GtkCellRenderer *cell;
667   struct variable *var;
668
669   var = g_object_get_data (G_OBJECT (column), "variable");
670   g_return_if_fail (var != NULL);
671
672   if (var_has_value_labels (var))
673     {
674       cell = gtk_cell_renderer_combo_new ();
675       g_object_set (G_OBJECT (cell),
676                     "has-entry", TRUE,
677                     "text-column", 0,
678                     NULL);
679     }
680   else
681     cell = gtk_cell_renderer_text_new ();
682
683   g_signal_connect (cell, "editing-started",
684                     G_CALLBACK (on_data_column_editing_started), NULL);
685   g_signal_connect (cell, "edited", G_CALLBACK (on_data_column_edited), NULL);
686
687   g_object_set_data (G_OBJECT (cell), "column", column);
688   g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
689
690   pspp_sheet_view_column_clear (column);
691   pspp_sheet_view_column_pack_start (column, cell, TRUE);
692
693   pspp_sheet_view_column_set_cell_data_func (
694     column, cell, render_data_cell, data_sheet, NULL);
695 }
696
697 static PsppSheetViewColumn *
698 make_data_column (PsppireDataSheet *data_sheet, gint dict_idx,
699                   gint base_width, gint incr_width)
700 {
701   PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
702   struct variable *var;
703   PsppSheetViewColumn *column;
704   char *name;
705   int width;
706
707   var = psppire_dict_get_variable (data_store->dict, dict_idx);
708
709   column = pspp_sheet_view_column_new ();
710
711   name = escape_underscores (var_get_name (var));
712   pspp_sheet_view_column_set_title (column, name);
713   free (name);
714
715   g_object_set_data (G_OBJECT (column), "variable", var);
716
717   width = display_width_to_pixel_width (data_sheet,
718                                         var_get_display_width (var),
719                                         base_width, incr_width);
720   pspp_sheet_view_column_set_min_width (column, 10);
721   pspp_sheet_view_column_set_fixed_width (column, width);
722   pspp_sheet_view_column_set_resizable (column, TRUE);
723
724   pspp_sheet_view_column_set_clickable (column, TRUE);
725   g_signal_connect (column, "notify::width",
726                     G_CALLBACK (on_data_column_resized), data_sheet);
727
728   g_signal_connect (column, "button-press-event",
729                     G_CALLBACK (on_column_button_press_event),
730                     data_sheet);
731   g_signal_connect (column, "query-tooltip",
732                     G_CALLBACK (on_data_column_query_tooltip), NULL);
733   g_signal_connect (column, "popup-menu",
734                     G_CALLBACK (on_data_column_popup_menu), data_sheet);
735
736   add_data_column_cell_renderer (data_sheet, column);
737
738   return column;
739 }
740
741 static void
742 make_new_variable_column (PsppireDataSheet *data_sheet,
743                           gint base_width, gint incr_width)
744 {
745   PsppSheetViewColumn *column;
746   GtkCellRenderer *cell;
747   int width;
748
749   cell = gtk_cell_renderer_text_new ();
750   g_object_set (cell, "editable", TRUE, NULL);
751
752   g_signal_connect (cell, "edited", G_CALLBACK (on_new_variable_column_edited),
753                     NULL);
754
755   column = pspp_sheet_view_column_new_with_attributes ("", cell, NULL);
756   g_object_set_data (G_OBJECT (column), "new-var-column", column);
757
758   width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width);
759   pspp_sheet_view_column_set_min_width (column, 10);
760   pspp_sheet_view_column_set_fixed_width (column, width);
761   pspp_sheet_view_column_set_tabbable (column, FALSE);
762
763   g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet);
764   g_signal_connect (column, "button-press-event",
765                     G_CALLBACK (on_column_button_press_event),
766                     data_sheet);
767   g_signal_connect (column, "popup-menu",
768                     G_CALLBACK (on_data_column_popup_menu), data_sheet);
769
770   pspp_sheet_view_column_set_visible (column, data_sheet->may_create_vars);
771
772   pspp_sheet_view_append_column (PSPP_SHEET_VIEW (data_sheet), column);
773   data_sheet->new_variable_column = column;
774 }
775
776 static void
777 psppire_data_sheet_model_changed (GObject    *gobject,
778                                   GParamSpec *pspec,
779                                   gpointer    user_data)
780 {
781   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (gobject);
782   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
783   PsppireDataStore *data_store;
784
785   /* Remove old columns. */
786   for (;;)
787     {
788       PsppSheetViewColumn *column = pspp_sheet_view_get_column (sheet_view, 0);
789       if (column == NULL)
790         break;
791
792       pspp_sheet_view_remove_column (sheet_view, column);
793     }
794   data_sheet->new_variable_column = NULL;
795
796   if (pspp_sheet_view_get_model (sheet_view) == NULL)
797     {
798       /* Don't create any columns at all if there's no model.  Otherwise we'll
799          create some columns as part of the "dispose" callback for the sheet
800          view, which sets the model to NULL.  That causes warnings to be
801          logged and is obviously undesirable in any case. */
802       return;
803     }
804
805   /* Add new columns. */
806   data_store = psppire_data_sheet_get_data_store (data_sheet);
807   if (data_store != NULL)
808     {
809       gint base_width, incr_width;
810       int i;
811
812       calc_width_conversion (data_sheet, &base_width, &incr_width);
813
814       make_row_number_column (data_sheet, data_store);
815       for (i = 0; i < psppire_dict_get_var_cnt (data_store->dict); i++)
816         {
817           PsppSheetViewColumn *column;
818
819           column = make_data_column (data_sheet, i, base_width, incr_width);
820           pspp_sheet_view_append_column (sheet_view, column);
821         }
822       make_new_variable_column (data_sheet, base_width, incr_width);
823     }
824 }
825
826 enum
827   {
828     PROP_0,
829     PROP_DATA_STORE,
830     PROP_VALUE_LABELS,
831     PROP_CASE_NUMBERS,
832     PROP_CURRENT_CASE,
833     PROP_MAY_CREATE_VARS,
834     PROP_MAY_DELETE_VARS,
835     PROP_UI_MANAGER
836   };
837
838 static void
839 psppire_data_sheet_set_property (GObject      *object,
840                                  guint         prop_id,
841                                  const GValue *value,
842                                  GParamSpec   *pspec)
843 {
844   PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
845
846   switch (prop_id)
847     {
848     case PROP_DATA_STORE:
849       psppire_data_sheet_set_data_store (
850         obj, PSPPIRE_DATA_STORE (g_value_get_object (value)));
851       break;
852
853     case PROP_VALUE_LABELS:
854       psppire_data_sheet_set_value_labels (obj, g_value_get_boolean (value));
855       break;
856
857     case PROP_CASE_NUMBERS:
858       psppire_data_sheet_set_case_numbers (obj, g_value_get_boolean (value));
859       break;
860
861     case PROP_CURRENT_CASE:
862       psppire_data_sheet_goto_case (obj, g_value_get_long (value));
863       break;
864
865     case PROP_MAY_CREATE_VARS:
866       psppire_data_sheet_set_may_create_vars (obj,
867                                               g_value_get_boolean (value));
868       break;
869
870     case PROP_MAY_DELETE_VARS:
871       psppire_data_sheet_set_may_delete_vars (obj,
872                                               g_value_get_boolean (value));
873       break;
874
875     case PROP_UI_MANAGER:
876     default:
877       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
878       break;
879     }
880 }
881
882 static void
883 psppire_data_sheet_get_property (GObject      *object,
884                                  guint         prop_id,
885                                  GValue       *value,
886                                  GParamSpec   *pspec)
887 {
888   PsppireDataSheet *obj = PSPPIRE_DATA_SHEET (object);
889
890   switch (prop_id)
891     {
892     case PROP_DATA_STORE:
893       g_value_set_object (value, psppire_data_sheet_get_data_store (obj));
894       break;
895
896     case PROP_VALUE_LABELS:
897       g_value_set_boolean (value, psppire_data_sheet_get_value_labels (obj));
898       break;
899
900     case PROP_CASE_NUMBERS:
901       g_value_set_boolean (value, psppire_data_sheet_get_case_numbers (obj));
902       break;
903
904     case PROP_CURRENT_CASE:
905       g_value_set_long (value, psppire_data_sheet_get_selected_case (obj));
906       break;
907
908     case PROP_MAY_CREATE_VARS:
909       g_value_set_boolean (value, obj->may_create_vars);
910       break;
911
912     case PROP_MAY_DELETE_VARS:
913       g_value_set_boolean (value, obj->may_delete_vars);
914       break;
915
916     case PROP_UI_MANAGER:
917       g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj));
918       break;
919
920     default:
921       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
922       break;
923     }
924 }
925
926 gboolean
927 psppire_data_sheet_get_value_labels (const PsppireDataSheet *ds)
928 {
929   return ds->show_value_labels;
930 }
931
932 void
933 psppire_data_sheet_set_value_labels (PsppireDataSheet *ds,
934                                   gboolean show_value_labels)
935 {
936   show_value_labels = !!show_value_labels;
937   if (show_value_labels != ds->show_value_labels)
938     {
939       ds->show_value_labels = show_value_labels;
940       g_object_notify (G_OBJECT (ds), "value-labels");
941       gtk_widget_queue_draw (GTK_WIDGET (ds));
942
943       /* Make the cell being edited refresh too. */
944       pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (ds), TRUE);
945     }
946 }
947
948 gboolean
949 psppire_data_sheet_get_case_numbers (const PsppireDataSheet *ds)
950 {
951   return ds->show_case_numbers;
952 }
953
954 void
955 psppire_data_sheet_set_case_numbers (PsppireDataSheet *ds,
956                                      gboolean show_case_numbers)
957 {
958   show_case_numbers = !!show_case_numbers;
959   if (show_case_numbers != ds->show_case_numbers)
960     {
961       PsppSheetViewColumn *column;
962
963       ds->show_case_numbers = show_case_numbers;
964       column = pspp_sheet_view_get_column (PSPP_SHEET_VIEW (ds), 0);
965       if (column)
966         pspp_sheet_view_column_set_visible (column, show_case_numbers);
967
968       g_object_notify (G_OBJECT (ds), "case-numbers");
969       gtk_widget_queue_draw (GTK_WIDGET (ds));
970     }
971 }
972
973 gboolean
974 psppire_data_sheet_get_may_create_vars (PsppireDataSheet *data_sheet)
975 {
976   return data_sheet->may_create_vars;
977 }
978
979 void
980 psppire_data_sheet_set_may_create_vars (PsppireDataSheet *data_sheet,
981                                        gboolean may_create_vars)
982 {
983   if (data_sheet->may_create_vars != may_create_vars)
984     {
985       data_sheet->may_create_vars = may_create_vars;
986       if (data_sheet->new_variable_column)
987         pspp_sheet_view_column_set_visible (data_sheet->new_variable_column,
988                                             may_create_vars);
989
990       on_selection_changed (pspp_sheet_view_get_selection (
991                               PSPP_SHEET_VIEW (data_sheet)), NULL);
992     }
993 }
994
995 gboolean
996 psppire_data_sheet_get_may_delete_vars (PsppireDataSheet *data_sheet)
997 {
998   return data_sheet->may_delete_vars;
999 }
1000
1001 void
1002 psppire_data_sheet_set_may_delete_vars (PsppireDataSheet *data_sheet,
1003                                        gboolean may_delete_vars)
1004 {
1005   if (data_sheet->may_delete_vars != may_delete_vars)
1006     {
1007       data_sheet->may_delete_vars = may_delete_vars;
1008       on_selection_changed (pspp_sheet_view_get_selection (
1009                               PSPP_SHEET_VIEW (data_sheet)), NULL);
1010     }
1011 }
1012
1013 static PsppSheetViewColumn *
1014 psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet,
1015                                              gint dict_index)
1016 {
1017   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1018   PsppireDataStore *data_store;
1019   PsppSheetViewColumn *column;
1020   struct variable *var;
1021   GList *list, *iter;
1022
1023   data_store = psppire_data_sheet_get_data_store (data_sheet);
1024   g_return_val_if_fail (data_store != NULL, NULL);
1025   g_return_val_if_fail (data_store->dict != NULL, NULL);
1026
1027   var = psppire_dict_get_variable (data_store->dict, dict_index);
1028   g_return_val_if_fail (var != NULL, NULL);
1029
1030   column = NULL;
1031   list = pspp_sheet_view_get_columns (sheet_view);
1032   for (iter = list; iter != NULL; iter = iter->next)
1033     {
1034       PsppSheetViewColumn *c = iter->data;
1035       struct variable *v;
1036
1037       v = g_object_get_data (G_OBJECT (c), "variable");
1038       if (v == var)
1039         {
1040           column = c;
1041           break;
1042         }
1043     }
1044   g_list_free (list);
1045
1046   return column;
1047 }
1048
1049 void
1050 psppire_data_sheet_show_variable (PsppireDataSheet *data_sheet,
1051                                   gint dict_index)
1052 {
1053   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1054   PsppSheetViewColumn *column;
1055
1056   column = psppire_data_sheet_find_column_for_variable (data_sheet,
1057                                                         dict_index);
1058   if (column != NULL)
1059     pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column,
1060                                     FALSE, 0.0, 0.0);
1061 }
1062
1063 struct variable *
1064 psppire_data_sheet_get_current_variable (const PsppireDataSheet *data_sheet)
1065 {
1066   PsppSheetSelection *selection;
1067   struct variable *var;
1068   GList *selected_columns;
1069   GList *iter;
1070
1071   selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (data_sheet));
1072   selected_columns = pspp_sheet_selection_get_selected_columns (selection);
1073
1074   var = NULL;
1075   for (iter = selected_columns; iter != NULL; iter = iter->next)
1076     {
1077       PsppSheetViewColumn *column = iter->data;
1078       struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
1079       if (v != NULL)
1080         {
1081           if (var)
1082             {
1083               var = NULL;
1084               break;
1085             }
1086           else
1087             var = v;
1088         }
1089     }
1090
1091   g_list_free (selected_columns);
1092
1093   return var;
1094
1095 }
1096 void
1097 psppire_data_sheet_goto_case (PsppireDataSheet *data_sheet, gint case_index)
1098 {
1099   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1100   PsppireDataStore *store = data_sheet->data_store;
1101   PsppSheetSelection *selection;
1102   GtkTreePath *path;
1103
1104   g_return_if_fail (case_index >= 0);
1105   g_return_if_fail (case_index < psppire_data_store_get_case_count (store));
1106
1107   path = gtk_tree_path_new_from_indices (case_index, -1);
1108
1109   /* Select the case. */
1110   selection = pspp_sheet_view_get_selection (sheet_view);
1111   pspp_sheet_selection_unselect_all (selection);
1112   pspp_sheet_selection_select_path (selection, path);
1113   pspp_sheet_selection_select_all_columns (selection);
1114
1115   /* Scroll so that the case is visible. */
1116   pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1117
1118   gtk_tree_path_free (path);
1119 }
1120
1121 /* Returns the 0-based index of a selected case, if there is at least one, and
1122    -1 otherwise.
1123
1124    If more than one case is selected, returns the one with the smallest index,
1125    that is, the index of the case closest to the beginning of the file.  The
1126    row that can be used to insert a new case is not considered a case. */
1127 gint
1128 psppire_data_sheet_get_selected_case (const PsppireDataSheet *data_sheet)
1129 {
1130   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1131   PsppireDataStore *store = data_sheet->data_store;
1132   const struct range_set_node *node;
1133   PsppSheetSelection *selection;
1134   struct range_set *rows;
1135   gint row;
1136
1137   selection = pspp_sheet_view_get_selection (sheet_view);
1138   rows = pspp_sheet_selection_get_range_set (selection);
1139   node = range_set_first (rows);
1140   row = (node && node->start < psppire_data_store_get_case_count (store)
1141          ? node->start
1142          : -1);
1143   range_set_destroy (rows);
1144
1145   return row;
1146 }
1147
1148 /* Returns the 0-based index of a selected case, if exactly one case is
1149    selected, and -1 otherwise.  Returns -1 if the row that can be used to
1150    insert a new case is selected. */
1151 gint
1152 psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet)
1153 {
1154   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1155   PsppireDataStore *store = data_sheet->data_store;
1156   const struct range_set_node *node;
1157   PsppSheetSelection *selection;
1158   struct range_set *rows;
1159   gint row;
1160
1161   selection = pspp_sheet_view_get_selection (sheet_view);
1162   if (pspp_sheet_selection_count_selected_rows (selection) != 1)
1163     return -1;
1164
1165   rows = pspp_sheet_selection_get_range_set (selection);
1166   node = range_set_first (rows);
1167   row = (node && node->start < psppire_data_store_get_case_count (store)
1168          ? node->start
1169          : -1);
1170   range_set_destroy (rows);
1171
1172   return row;
1173 }
1174
1175 GtkUIManager *
1176 psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet)
1177 {
1178   if (data_sheet->uim == NULL)
1179     {
1180       data_sheet->uim = 
1181         GTK_UI_MANAGER (get_object_assert (data_sheet->builder,
1182                                            "data_sheet_uim",
1183                                            GTK_TYPE_UI_MANAGER));
1184       g_object_ref (data_sheet->uim);
1185     }
1186
1187   return data_sheet->uim;
1188 }
1189
1190 static void
1191 psppire_data_sheet_dispose (GObject *object)
1192 {
1193   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object);
1194
1195   if (data_sheet->dispose_has_run)
1196     return;
1197
1198   data_sheet->dispose_has_run = TRUE;
1199
1200   psppire_data_sheet_unset_data_store (data_sheet);
1201
1202   g_object_unref (data_sheet->builder);
1203
1204   if (data_sheet->uim)
1205     g_object_unref (data_sheet->uim);
1206
1207   G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1208 }
1209
1210 static void
1211 psppire_data_sheet_map (GtkWidget *widget)
1212 {
1213   GtkClipboard *clip;
1214
1215   GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
1216
1217   clip = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
1218   g_signal_connect (clip, "owner-change", G_CALLBACK (on_owner_change),
1219                     widget);
1220   on_owner_change (clip, NULL, widget);
1221 }
1222
1223 static void
1224 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1225 {
1226   GObjectClass *gobject_class;
1227   GtkWidgetClass *widget_class;
1228
1229   gobject_class = G_OBJECT_CLASS (class);
1230   gobject_class->set_property = psppire_data_sheet_set_property;
1231   gobject_class->get_property = psppire_data_sheet_get_property;
1232   gobject_class->dispose = psppire_data_sheet_dispose;
1233
1234   widget_class = GTK_WIDGET_CLASS (class);
1235   widget_class->map = psppire_data_sheet_map;
1236
1237   g_signal_new ("var-double-clicked",
1238                 G_OBJECT_CLASS_TYPE (gobject_class),
1239                 G_SIGNAL_RUN_LAST,
1240                 0,
1241                 g_signal_accumulator_true_handled, NULL,
1242                 psppire_marshal_BOOLEAN__INT,
1243                 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1244
1245   g_object_class_install_property (
1246     gobject_class, PROP_DATA_STORE,
1247     g_param_spec_object ("data-store",
1248                          "Data Store",
1249                          "The data store for the data sheet to display.",
1250                          PSPPIRE_TYPE_DATA_STORE,
1251                          G_PARAM_WRITABLE | G_PARAM_READABLE));
1252
1253   g_object_class_install_property (
1254     gobject_class, PROP_VALUE_LABELS,
1255     g_param_spec_boolean ("value-labels",
1256                           "Value Labels",
1257                           "Whether or not the data sheet should display labels instead of values",
1258                           FALSE,
1259                           G_PARAM_WRITABLE | G_PARAM_READABLE));
1260
1261   g_object_class_install_property (
1262     gobject_class, PROP_CASE_NUMBERS,
1263     g_param_spec_boolean ("case-numbers",
1264                           "Case Numbers",
1265                           "Whether or not the data sheet should display case numbers",
1266                           FALSE,
1267                           G_PARAM_WRITABLE | G_PARAM_READABLE));
1268
1269   g_object_class_install_property (
1270     gobject_class,
1271     PROP_CURRENT_CASE,
1272     g_param_spec_long ("current-case",
1273                        "Current Case",
1274                        "Zero based number of the selected case",
1275                        0, CASENUMBER_MAX,
1276                        0,
1277                        G_PARAM_WRITABLE | G_PARAM_READABLE));
1278
1279   g_object_class_install_property (
1280     gobject_class,
1281     PROP_MAY_CREATE_VARS,
1282     g_param_spec_boolean ("may-create-vars",
1283                           "May create variables",
1284                           "Whether the user may create more variables",
1285                           TRUE,
1286                           G_PARAM_READWRITE));
1287
1288   g_object_class_install_property (
1289     gobject_class,
1290     PROP_MAY_DELETE_VARS,
1291     g_param_spec_boolean ("may-delete-vars",
1292                           "May delete variables",
1293                           "Whether the user may delete variables",
1294                           TRUE,
1295                           G_PARAM_READWRITE));
1296
1297   g_object_class_install_property (
1298     gobject_class,
1299     PROP_UI_MANAGER,
1300     g_param_spec_object ("ui-manager",
1301                          "UI Manager",
1302                          "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.",
1303                          GTK_TYPE_UI_MANAGER,
1304                          G_PARAM_READABLE));
1305 }
1306
1307 static void
1308 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1309 {
1310   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1311   GtkWidget *menu;
1312
1313   menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1314   gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1315 }
1316
1317 static void
1318 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1319 {
1320   do_popup_menu (widget, 0, gtk_get_current_event_time ());
1321 }
1322
1323 static gboolean
1324 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1325                    gpointer user_data UNUSED)
1326 {
1327   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1328
1329   if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1330     {
1331       PsppSheetSelection *selection;
1332
1333       selection = pspp_sheet_view_get_selection (sheet_view);
1334       if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1335         {
1336           GtkTreePath *path;
1337
1338           if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1339                                                &path, NULL, NULL, NULL))
1340             {
1341               pspp_sheet_selection_unselect_all (selection);
1342               pspp_sheet_selection_select_path (selection, path);
1343               pspp_sheet_selection_select_all_columns (selection);
1344               gtk_tree_path_free (path);
1345             }
1346         }
1347
1348       do_popup_menu (widget, event->button, event->time);
1349
1350       return TRUE;
1351     }
1352
1353   return FALSE;
1354 }
1355
1356 static void
1357 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1358 {
1359   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1360   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1361   const struct range_set_node *node;
1362   struct range_set *selected;
1363
1364   selected = pspp_sheet_selection_get_range_set (selection);
1365   for (node = range_set_last (selected); node != NULL;
1366        node = range_set_prev (selected, node))
1367     {
1368       unsigned long int start = range_set_node_get_start (node);
1369       unsigned long int count = range_set_node_get_width (node);
1370
1371       psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1372     }
1373   range_set_destroy (selected);
1374 }
1375
1376 static void
1377 on_selection_changed (PsppSheetSelection *selection,
1378                       gpointer user_data UNUSED)
1379 {
1380   PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1381   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1382   gint n_selected_rows;
1383   gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1384   GList *list, *iter;
1385   GtkTreePath *path;
1386   GtkAction *action;
1387
1388   n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1389
1390   action = get_action_assert (data_sheet->builder, "edit_insert-case");
1391   gtk_action_set_sensitive (action, n_selected_rows > 0);
1392
1393   switch (n_selected_rows)
1394     {
1395     case 0:
1396       may_delete_cases = FALSE;
1397       break;
1398
1399     case 1:
1400       /* The row used for inserting new cases cannot be deleted. */
1401       path = gtk_tree_path_new_from_indices (
1402         psppire_data_store_get_case_count (data_sheet->data_store), -1);
1403       may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1404                                                                  path);
1405       gtk_tree_path_free (path);
1406       break;
1407
1408     default:
1409       may_delete_cases = TRUE;
1410       break;
1411     }
1412   action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1413   gtk_action_set_sensitive (action, may_delete_cases);
1414
1415   may_delete_vars = may_insert_vars = FALSE;
1416   list = pspp_sheet_selection_get_selected_columns (selection);
1417   for (iter = list; iter != NULL; iter = iter->next)
1418     {
1419       PsppSheetViewColumn *column = iter->data;
1420       struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1421
1422       if (var != NULL)
1423         {
1424           may_delete_vars = may_insert_vars = TRUE;
1425           break;
1426         }
1427       if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1428         may_insert_vars = TRUE;
1429     }
1430   g_list_free (list);
1431
1432   may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1433   may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1434
1435   action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1436   gtk_action_set_sensitive (action, may_insert_vars);
1437
1438   action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1439   gtk_action_set_sensitive (action, may_delete_vars);
1440
1441   action = get_action_assert (data_sheet->builder, "sort-up");
1442   gtk_action_set_sensitive (action, may_delete_vars);
1443
1444   action = get_action_assert (data_sheet->builder, "sort-down");
1445   gtk_action_set_sensitive (action, may_delete_vars);
1446
1447   psppire_data_sheet_update_clip_actions (data_sheet);
1448 }
1449
1450 static gboolean
1451 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1452                                     struct range_set **rowsp,
1453                                     struct range_set **colsp)
1454 {
1455   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1456   PsppireDataStore *data_store = data_sheet->data_store;
1457   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1458   unsigned long n_cases;
1459   struct range_set *rows, *cols;
1460   GList *list, *iter;
1461
1462   if (data_store == NULL)
1463     return FALSE;
1464   n_cases = psppire_data_store_get_case_count (data_store);
1465
1466   rows = pspp_sheet_selection_get_range_set (selection);
1467   range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1468   if (range_set_is_empty (rows))
1469     {
1470       range_set_destroy (rows);
1471       return FALSE;
1472     }
1473
1474   cols = range_set_create ();
1475   list = pspp_sheet_selection_get_selected_columns (selection);
1476   for (iter = list; iter != NULL; iter = iter->next)
1477     {
1478       PsppSheetViewColumn *column = iter->data;
1479       struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1480
1481       if (var != NULL)
1482         range_set_set1 (cols, var_get_dict_index (var), 1);
1483     }
1484   g_list_free (list);
1485   if (range_set_is_empty (cols))
1486     {
1487       range_set_destroy (rows);
1488       range_set_destroy (cols);
1489       return FALSE;
1490     }
1491
1492   *rowsp = rows;
1493   *colsp = cols;
1494   return TRUE;
1495 }
1496
1497 static void
1498 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1499 {
1500   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1501   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1502   PsppireDataStore *data_store = data_sheet->data_store;
1503   struct range_set *selected;
1504   unsigned long row;
1505
1506   selected = pspp_sheet_selection_get_range_set (selection);
1507   row = range_set_scan (selected, 0);
1508   range_set_destroy (selected);
1509
1510   if (row <= psppire_data_store_get_case_count (data_store))
1511     psppire_data_store_insert_new_case (data_store, row);
1512 }
1513
1514 static void
1515 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1516 {
1517   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1518   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1519   PsppireDict *dict = data_sheet->data_store->dict;
1520   PsppSheetViewColumn *column;
1521   struct variable *var;
1522   gchar name[64];
1523   GList *list;
1524   gint index;
1525
1526   list = pspp_sheet_selection_get_selected_columns (selection);
1527   if (list == NULL)
1528     return;
1529   column = list->data;
1530   g_list_free (list);
1531
1532   var = g_object_get_data (G_OBJECT (column), "variable");
1533   index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1534   if (psppire_dict_generate_name (dict, name, sizeof name))
1535     psppire_dict_insert_variable (dict, index, name);
1536 }
1537
1538 static void
1539 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1540 {
1541   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1542   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1543   PsppireDict *dict = data_sheet->data_store->dict;
1544   GList *list, *iter;
1545
1546   list = pspp_sheet_selection_get_selected_columns (selection);
1547   if (list == NULL)
1548     return;
1549   list = g_list_reverse (list);
1550   for (iter = list; iter; iter = iter->next)
1551     {
1552       PsppSheetViewColumn *column = iter->data;
1553       struct variable *var;
1554
1555       var = g_object_get_data (G_OBJECT (column), "variable");
1556       if (var != NULL)
1557         psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1558     }
1559   g_list_free (list);
1560 }
1561
1562 enum sort_order
1563   {
1564     SORT_ASCEND,
1565     SORT_DESCEND
1566   };
1567
1568 static void
1569 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1570 {
1571   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1572   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1573   PsppireDataWindow *pdw;
1574   GList *list, *iter;
1575   GString *syntax;
1576   int n_vars;
1577
1578   pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1579   g_return_if_fail (pdw != NULL);
1580
1581   list = pspp_sheet_selection_get_selected_columns (selection);
1582
1583   syntax = g_string_new ("SORT CASES BY");
1584   n_vars = 0;
1585   for (iter = list; iter; iter = iter->next)
1586     {
1587       PsppSheetViewColumn *column = iter->data;
1588       struct variable *var;
1589
1590       var = g_object_get_data (G_OBJECT (column), "variable");
1591       if (var != NULL)
1592         {
1593           g_string_append_printf (syntax, " %s", var_get_name (var));
1594           n_vars++;
1595         }
1596     }
1597   if (n_vars > 0)
1598     {
1599       if (order == SORT_DESCEND)
1600         g_string_append (syntax, " (DOWN)");
1601       g_string_append_c (syntax, '.');
1602       execute_const_syntax_string (pdw, syntax->str);
1603     }
1604   g_string_free (syntax, TRUE);
1605 }
1606
1607 void
1608 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1609 {
1610   do_sort (data_sheet, SORT_ASCEND);
1611 }
1612
1613 void
1614 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1615 {
1616   do_sort (data_sheet, SORT_DESCEND);
1617 }
1618
1619 void
1620 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1621 {
1622   goto_case_dialog (data_sheet);
1623 }
1624
1625 void
1626 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1627 {
1628   PsppireDataWindow *pdw;
1629
1630   pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1631   g_return_if_fail (pdw != NULL);
1632
1633   find_dialog (pdw);
1634 }
1635
1636 void
1637 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1638 {
1639   psppire_data_sheet_set_clip (data_sheet, FALSE);
1640 }
1641
1642 void
1643 on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet)
1644 {
1645   psppire_data_sheet_set_clip (data_sheet, TRUE);
1646 }
1647
1648 void
1649 on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet)
1650 {
1651   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
1652   GtkClipboard *clipboard =
1653     gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1654
1655   gtk_clipboard_request_contents (clipboard,
1656                                   gdk_atom_intern ("UTF8_STRING", TRUE),
1657                                   psppire_data_sheet_clip_received_cb,
1658                                   data_sheet);
1659 }
1660
1661 static void
1662 psppire_data_sheet_init (PsppireDataSheet *obj)
1663 {
1664   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1665   GtkAction *action;
1666
1667   obj->show_value_labels = FALSE;
1668   obj->show_case_numbers = TRUE;
1669   obj->may_create_vars = TRUE;
1670   obj->may_delete_vars = TRUE;
1671
1672   obj->scroll_to_bottom_signal = 0;
1673   obj->scroll_to_right_signal = 0;
1674   obj->new_variable_column = NULL;
1675   obj->container = NULL;
1676
1677   obj->uim = NULL;
1678   obj->dispose_has_run = FALSE;
1679
1680   pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1681
1682   g_signal_connect (obj, "notify::model",
1683                     G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1684
1685   pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1686   pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1687                                  PSPP_SHEET_SELECTION_RECTANGLE);
1688
1689   g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1690   g_signal_connect (obj, "query-tooltip",
1691                     G_CALLBACK (on_query_tooltip), NULL);
1692   g_signal_connect (obj, "button-press-event",
1693                     G_CALLBACK (on_button_pressed), NULL);
1694   g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1695
1696   obj->builder = builder_new ("data-sheet.ui");
1697
1698   action = get_action_assert (obj->builder, "edit_clear-cases");
1699   g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1700                     obj);
1701   gtk_action_set_sensitive (action, FALSE);
1702   g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1703                     "changed", G_CALLBACK (on_selection_changed), NULL);
1704
1705   action = get_action_assert (obj->builder, "edit_insert-case");
1706   g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1707                     obj);
1708
1709   action = get_action_assert (obj->builder, "edit_insert-variable");
1710   g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1711                     obj);
1712
1713   action = get_action_assert (obj->builder, "edit_goto-case");
1714   g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1715                     obj);
1716
1717   action = get_action_assert (obj->builder, "edit_copy");
1718   g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1719
1720   action = get_action_assert (obj->builder, "edit_cut");
1721   g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj);
1722
1723   action = get_action_assert (obj->builder, "edit_paste");
1724   g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj);
1725
1726   action = get_action_assert (obj->builder, "edit_clear-variables");
1727   g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1728                     obj);
1729
1730   action = get_action_assert (obj->builder, "edit_find");
1731   g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1732
1733   action = get_action_assert (obj->builder, "sort-up");
1734   g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1735
1736   action = get_action_assert (obj->builder, "sort-down");
1737   g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1738
1739 }
1740
1741 GtkWidget *
1742 psppire_data_sheet_new (void)
1743 {
1744   return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1745 }
1746
1747 PsppireDataStore *
1748 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1749 {
1750   return data_sheet->data_store;
1751 }
1752
1753 static void
1754 refresh_model (PsppireDataSheet *data_sheet)
1755 {
1756   pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1757
1758   if (data_sheet->data_store != NULL)
1759     {
1760       PsppireEmptyListStore *model;
1761       GtkAction *action;
1762       int n_rows;
1763
1764       n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1765       model = psppire_empty_list_store_new (n_rows);
1766       pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1767                                  GTK_TREE_MODEL (model));
1768       g_object_unref (model);
1769
1770       action = get_action_assert (data_sheet->builder, "edit_copy");
1771       g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1772                         data_sheet);
1773     }
1774 }
1775
1776 static void
1777 on_case_inserted (PsppireDataStore *data_store, gint row,
1778                   PsppireDataSheet *data_sheet)
1779 {
1780   PsppireEmptyListStore *empty_list_store;
1781   GtkTreeModel *tree_model;
1782   gint n_rows;
1783
1784   g_return_if_fail (data_store == data_sheet->data_store);
1785
1786   n_rows = psppire_data_store_get_case_count (data_store) + 1;
1787   if (row == n_rows - 1)
1788     row++;
1789
1790   tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1791   empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1792   psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1793   psppire_empty_list_store_row_inserted (empty_list_store, row);
1794 }
1795
1796 static void
1797 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1798                   PsppireDataSheet *data_sheet)
1799 {
1800
1801   g_return_if_fail (data_store == data_sheet->data_store);
1802
1803   if (n_cases > 1)
1804     {
1805       /* This is a bit of a cop-out.  We could do better, if it ever turns out
1806          that this performs too poorly. */
1807       refresh_model (data_sheet);
1808     }
1809   else
1810     {
1811       PsppireEmptyListStore *empty_list_store;
1812       GtkTreeModel *tree_model;
1813       gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1814
1815       tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1816       empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1817       psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1818       psppire_empty_list_store_row_deleted (empty_list_store, first);
1819     }
1820 }
1821
1822 static void
1823 on_case_change (PsppireDataStore *data_store, gint row,
1824                 PsppireDataSheet *data_sheet)
1825 {
1826   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1827
1828   pspp_sheet_view_stop_editing (sheet_view, TRUE);
1829   gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1830 }
1831
1832 static void
1833 on_backend_changed (PsppireDataStore *data_store,
1834                     PsppireDataSheet *data_sheet)
1835 {
1836   g_return_if_fail (data_store == data_sheet->data_store);
1837   refresh_model (data_sheet);
1838 }
1839
1840 static void
1841 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1842                                    PsppireDataSheet *data_sheet)
1843 {
1844   PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1845   PsppSheetViewColumn *column;
1846   struct variable *var;
1847   int display_width;
1848   gint pixel_width;
1849
1850   g_return_if_fail (data_sheet->data_store != NULL);
1851   g_return_if_fail (dict == data_sheet->data_store->dict);
1852
1853   column = psppire_data_sheet_find_column_for_variable (data_sheet,
1854                                                         dict_index);
1855   if (column == NULL)
1856     return;
1857
1858   var = psppire_dict_get_variable (data_store->dict, dict_index);
1859   g_return_if_fail (var != NULL);
1860
1861   pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1862   display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1863   if (display_width != var_get_display_width (var))
1864     {
1865       gint base_width, incr_width;
1866
1867       display_width = var_get_display_width (var);
1868       calc_width_conversion (data_sheet, &base_width, &incr_width);
1869       pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1870                                                   base_width, incr_width);
1871       pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1872     }
1873 }
1874
1875 static void
1876 on_variable_changed (PsppireDict *dict, int dict_index,
1877                      PsppireDataSheet *data_sheet)
1878 {
1879   PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1880   PsppSheetViewColumn *column;
1881   GtkCellRenderer *cell;
1882   struct variable *var;
1883   GList *cells;
1884   char *name;
1885
1886   g_return_if_fail (data_sheet->data_store != NULL);
1887   g_return_if_fail (dict == data_sheet->data_store->dict);
1888
1889   column = psppire_data_sheet_find_column_for_variable (data_sheet,
1890                                                         dict_index);
1891   if (column == NULL)
1892     return;
1893
1894   var = psppire_dict_get_variable (data_store->dict, dict_index);
1895   g_return_if_fail (var != NULL);
1896
1897   name = escape_underscores (var_get_name (var));
1898   if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1899     pspp_sheet_view_column_set_title (column, name);
1900   free (name);
1901
1902   cells = pspp_sheet_view_column_get_cell_renderers (column);
1903   g_return_if_fail (cells);
1904   cell = cells->data;
1905   g_list_free (cells);
1906
1907   if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1908     {
1909       /* Stop editing before we delete and replace the cell renderers.
1910          Otherwise if this column is currently being edited, an eventual call
1911          to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1912          that to gtk_cell_renderer_stop_editing(), which causes a critical.
1913
1914          It's possible that this is a bug in PsppSheetView, and it's possible
1915          that PsppSheetView inherits that from GtkTreeView, but I haven't
1916          investigated yet. */
1917       pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1918
1919       add_data_column_cell_renderer (data_sheet, column);
1920     }
1921 }
1922
1923 static void
1924 on_variable_inserted (PsppireDict *dict, int var_index,
1925                       PsppireDataSheet *data_sheet)
1926 {
1927   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1928   gint base_width, incr_width;
1929   PsppSheetViewColumn *column;
1930
1931   calc_width_conversion (data_sheet, &base_width, &incr_width);
1932   column = make_data_column (data_sheet, var_index, base_width, incr_width);
1933   pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1934 }
1935
1936 static void
1937 on_variable_deleted (PsppireDict *dict,
1938                      const struct variable *var, int case_idx, int width,
1939                      PsppireDataSheet *data_sheet)
1940 {
1941   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1942   GList *columns, *iter;
1943
1944   columns = pspp_sheet_view_get_columns (sheet_view);
1945   for (iter = columns; iter != NULL; iter = iter->next)
1946     {
1947       PsppSheetViewColumn *column = iter->data;
1948       const struct variable *column_var;
1949
1950       column_var = g_object_get_data (G_OBJECT (column), "variable");
1951       if (column_var == var)
1952         pspp_sheet_view_remove_column (sheet_view, column);
1953     }
1954   g_list_free (columns);
1955 }
1956
1957 static void
1958 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1959 {
1960   PsppireDataStore *store = data_sheet->data_store;
1961
1962   if (store == NULL)
1963     return;
1964
1965   data_sheet->data_store = NULL;
1966
1967   g_signal_handlers_disconnect_by_func (
1968     store, G_CALLBACK (on_backend_changed), data_sheet);
1969   g_signal_handlers_disconnect_by_func (
1970     store, G_CALLBACK (on_case_inserted), data_sheet);
1971   g_signal_handlers_disconnect_by_func (
1972     store, G_CALLBACK (on_cases_deleted), data_sheet);
1973   g_signal_handlers_disconnect_by_func (
1974     store, G_CALLBACK (on_case_change), data_sheet);
1975
1976   g_signal_handlers_disconnect_by_func (
1977     store->dict, G_CALLBACK (on_variable_changed), data_sheet);
1978   g_signal_handlers_disconnect_by_func (
1979     store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
1980   g_signal_handlers_disconnect_by_func (
1981     store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
1982   g_signal_handlers_disconnect_by_func (
1983     store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
1984
1985   g_object_unref (store);
1986 }
1987
1988 void
1989 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
1990                                 PsppireDataStore *data_store)
1991 {
1992   psppire_data_sheet_unset_data_store (data_sheet);
1993
1994   data_sheet->data_store = data_store;
1995   if (data_store != NULL)
1996     {
1997       g_object_ref (data_store);
1998       g_signal_connect (data_store, "backend-changed",
1999                         G_CALLBACK (on_backend_changed), data_sheet);
2000       g_signal_connect (data_store, "case-inserted",
2001                         G_CALLBACK (on_case_inserted), data_sheet);
2002       g_signal_connect (data_store, "cases-deleted",
2003                         G_CALLBACK (on_cases_deleted), data_sheet);
2004       g_signal_connect (data_store, "case-changed",
2005                         G_CALLBACK (on_case_change), data_sheet);
2006
2007       /* XXX it's unclean to hook into the dict this way--what if the dict
2008          changes?  As of this writing, though, nothing ever changes the
2009          data_store's dict. */
2010       g_signal_connect (data_store->dict, "variable-changed",
2011                         G_CALLBACK (on_variable_changed),
2012                         data_sheet);
2013       g_signal_connect (data_store->dict, "variable-display-width-changed",
2014                         G_CALLBACK (on_variable_display_width_changed),
2015                         data_sheet);
2016       g_signal_connect (data_store->dict, "variable-inserted",
2017                         G_CALLBACK (on_variable_inserted), data_sheet);
2018       g_signal_connect (data_store->dict, "variable-deleted",
2019                         G_CALLBACK (on_variable_deleted), data_sheet);
2020     }
2021   refresh_model (data_sheet);
2022 }
2023 \f
2024 /* Clipboard stuff */
2025
2026 /* A casereader and dictionary holding the data currently in the clip */
2027 static struct casereader *clip_datasheet = NULL;
2028 static struct dictionary *clip_dict = NULL;
2029
2030
2031 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2032
2033 /* Set the clip from the currently selected range in DATA_SHEET.  If CUT is
2034    true, clears the original data from DATA_SHEET, otherwise leaves the
2035    original data in-place. */
2036 static void
2037 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2038                              gboolean cut)
2039 {
2040   struct casewriter *writer ;
2041   PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2042   struct case_map *map = NULL;
2043   struct range_set *rows, *cols;
2044   const struct range_set_node *node;
2045
2046   if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2047     return;
2048
2049
2050   /* Destroy any existing clip */
2051   if ( clip_datasheet )
2052     {
2053       casereader_destroy (clip_datasheet);
2054       clip_datasheet = NULL;
2055     }
2056
2057   if ( clip_dict )
2058     {
2059       dict_destroy (clip_dict);
2060       clip_dict = NULL;
2061     }
2062
2063   /* Construct clip dictionary. */
2064   clip_dict = dict_create (dict_get_encoding (ds->dict->dict));
2065   RANGE_SET_FOR_EACH (node, cols)
2066     {
2067       int dict_index;
2068
2069       for (dict_index = node->start; dict_index < node->end; dict_index++)
2070         {
2071           struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2072           dict_clone_var_assert (clip_dict, var);
2073         }
2074     }
2075
2076   /* Construct clip data. */
2077   map = case_map_by_name (ds->dict->dict, clip_dict);
2078   writer = autopaging_writer_create (dict_get_proto (clip_dict));
2079   RANGE_SET_FOR_EACH (node, rows)
2080     {
2081       unsigned long int row;
2082
2083       for (row = node->start; row < node->end; row++)
2084         {
2085           struct ccase *old = psppire_data_store_get_case (ds, row);
2086           if (old != NULL)
2087             casewriter_write (writer, case_map_execute (map, old));
2088           else
2089             casewriter_force_error (writer);
2090         }
2091     }
2092   case_map_destroy (map);
2093
2094   /* Clear data that we copied out, if we're doing a "cut". */
2095   if (cut && !casewriter_error (writer))
2096     {
2097       RANGE_SET_FOR_EACH (node, rows)
2098         {
2099           unsigned long int row;
2100
2101           for (row = node->start; row < node->end; row++)
2102             {
2103               const struct range_set_node *node2;
2104
2105               RANGE_SET_FOR_EACH (node2, cols)
2106                 {
2107                   int dict_index;
2108
2109                   for (dict_index = node2->start; dict_index < node2->end;
2110                        dict_index++)
2111                     {
2112                       struct variable *var;
2113
2114                       var = dict_get_var (ds->dict->dict, dict_index);
2115                       psppire_data_store_set_string (ds, "", row,
2116                                                      var, false);
2117                     }
2118                 }
2119             }
2120         }
2121     }
2122
2123   range_set_destroy (rows);
2124   range_set_destroy (cols);
2125
2126   clip_datasheet = casewriter_make_reader (writer);
2127
2128   psppire_data_sheet_update_clipboard (data_sheet);
2129 }
2130
2131 enum {
2132   SELECT_FMT_NULL,
2133   SELECT_FMT_TEXT,
2134   SELECT_FMT_HTML
2135 };
2136
2137
2138 /* Perform data_out for case CC, variable V, appending to STRING */
2139 static void
2140 data_out_g_string (GString *string, const struct variable *v,
2141                    const struct ccase *cc)
2142 {
2143   const struct fmt_spec *fs = var_get_print_format (v);
2144   const union value *val = case_data (cc, v);
2145
2146   char *s = data_out (val, var_get_encoding (v), fs);
2147
2148   g_string_append (string, s);
2149
2150   g_free (s);
2151 }
2152
2153 static GString *
2154 clip_to_text (void)
2155 {
2156   casenumber r;
2157   GString *string;
2158
2159   const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2160   const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2161   const size_t var_cnt = dict_get_var_cnt (clip_dict);
2162
2163   string = g_string_sized_new (10 * val_cnt * case_cnt);
2164
2165   for (r = 0 ; r < case_cnt ; ++r )
2166     {
2167       int c;
2168       struct ccase *cc;
2169
2170       cc = casereader_peek (clip_datasheet, r);
2171       if (cc == NULL)
2172         {
2173           g_warning ("Clipboard seems to have inexplicably shrunk");
2174           break;
2175         }
2176
2177       for (c = 0 ; c < var_cnt ; ++c)
2178         {
2179           const struct variable *v = dict_get_var (clip_dict, c);
2180           data_out_g_string (string, v, cc);
2181           if ( c < val_cnt - 1 )
2182             g_string_append (string, "\t");
2183         }
2184
2185       if ( r < case_cnt)
2186         g_string_append (string, "\n");
2187
2188       case_unref (cc);
2189     }
2190
2191   return string;
2192 }
2193
2194
2195 static GString *
2196 clip_to_html (void)
2197 {
2198   casenumber r;
2199   GString *string;
2200
2201   const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
2202   const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
2203   const size_t var_cnt = dict_get_var_cnt (clip_dict);
2204
2205   /* Guestimate the size needed */
2206   string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2207
2208   g_string_append (string,
2209                    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2210
2211   g_string_append (string, "<table>\n");
2212   for (r = 0 ; r < case_cnt ; ++r )
2213     {
2214       int c;
2215       struct ccase *cc = casereader_peek (clip_datasheet, r);
2216       if (cc == NULL)
2217         {
2218           g_warning ("Clipboard seems to have inexplicably shrunk");
2219           break;
2220         }
2221       g_string_append (string, "<tr>\n");
2222
2223       for (c = 0 ; c < var_cnt ; ++c)
2224         {
2225           const struct variable *v = dict_get_var (clip_dict, c);
2226           g_string_append (string, "<td>");
2227           data_out_g_string (string, v, cc);
2228           g_string_append (string, "</td>\n");
2229         }
2230
2231       g_string_append (string, "</tr>\n");
2232
2233       case_unref (cc);
2234     }
2235   g_string_append (string, "</table>\n");
2236
2237   return string;
2238 }
2239
2240
2241
2242 static void
2243 psppire_data_sheet_clipboard_get_cb (GtkClipboard     *clipboard,
2244                                      GtkSelectionData *selection_data,
2245                                      guint             info,
2246                                      gpointer          data)
2247 {
2248   GString *string = NULL;
2249
2250   switch (info)
2251     {
2252     case SELECT_FMT_TEXT:
2253       string = clip_to_text ();
2254       break;
2255     case SELECT_FMT_HTML:
2256       string = clip_to_html ();
2257       break;
2258     default:
2259       g_assert_not_reached ();
2260     }
2261
2262   gtk_selection_data_set (selection_data, selection_data->target,
2263                           8,
2264                           (const guchar *) string->str, string->len);
2265
2266   g_string_free (string, TRUE);
2267 }
2268
2269 static void
2270 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2271                                        gpointer data)
2272 {
2273   dict_destroy (clip_dict);
2274   clip_dict = NULL;
2275
2276   casereader_destroy (clip_datasheet);
2277   clip_datasheet = NULL;
2278 }
2279
2280
2281 static const GtkTargetEntry targets[] = {
2282   { "UTF8_STRING",   0, SELECT_FMT_TEXT },
2283   { "STRING",        0, SELECT_FMT_TEXT },
2284   { "TEXT",          0, SELECT_FMT_TEXT },
2285   { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2286   { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2287   { "text/plain",    0, SELECT_FMT_TEXT },
2288   { "text/html",     0, SELECT_FMT_HTML }
2289 };
2290
2291
2292
2293 static void
2294 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2295 {
2296   GtkClipboard *clipboard =
2297     gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2298                               GDK_SELECTION_CLIPBOARD);
2299
2300   if (!gtk_clipboard_set_with_owner (clipboard, targets,
2301                                      G_N_ELEMENTS (targets),
2302                                      psppire_data_sheet_clipboard_get_cb,
2303                                      psppire_data_sheet_clipboard_clear_cb,
2304                                      G_OBJECT (sheet)))
2305     psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2306 }
2307
2308 static void
2309 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2310 {
2311   struct range_set *rows, *cols;
2312   GtkAction *action;
2313   gboolean enable;
2314
2315   enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2316   if (enable)
2317     {
2318       range_set_destroy (rows);
2319       range_set_destroy (cols);
2320     }
2321
2322   action = get_action_assert (data_sheet->builder, "edit_copy");
2323   gtk_action_set_sensitive (action, enable);
2324
2325   action = get_action_assert (data_sheet->builder, "edit_cut");
2326   gtk_action_set_sensitive (action, enable);
2327 }
2328 \f
2329 /* A callback for when the clipboard contents have been received. */
2330 static void
2331 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2332                                      GtkSelectionData *sd,
2333                                      gpointer data)
2334 {
2335   PsppireDataSheet *data_sheet = data;
2336   PsppireDataStore *store = data_sheet->data_store;
2337   struct range_set *rows, *cols;
2338   gint count = 0;
2339   gint next_row, next_column;
2340   gint first_column;
2341   char *c;
2342
2343   if ( sd->length < 0 )
2344     return;
2345
2346   if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
2347     return;
2348
2349   c = (char *) sd->data;
2350
2351   /* Get the starting selected position in the data sheet.  (Possibly we should
2352      only paste into the selected range if it's larger than one cell?) */
2353   if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2354     return;
2355   next_row = range_set_first (rows)->start;
2356   first_column = next_column = range_set_first (cols)->start;
2357   range_set_destroy (rows);
2358   range_set_destroy (cols);
2359
2360   g_return_if_fail (next_row >= 0);
2361   g_return_if_fail (next_column >= 0);
2362
2363   while (count < sd->length)
2364     {
2365       gint row = next_row;
2366       gint column = next_column;
2367       struct variable *var;
2368       char *s = c;
2369
2370       while (*c != '\t' && *c != '\n' && count < sd->length)
2371         {
2372           c++;
2373           count++;
2374         }
2375       if ( *c == '\t')
2376         {
2377           next_row = row ;
2378           next_column = column + 1;
2379         }
2380       else if ( *c == '\n')
2381         {
2382           next_row = row + 1;
2383           next_column = first_column;
2384         }
2385       *c++ = '\0';
2386       count++;
2387
2388       var = psppire_dict_get_variable (store->dict, column);
2389       if (var != NULL)
2390         psppire_data_store_set_string (store, s, row, var, FALSE);
2391     }
2392 }
2393
2394 static void
2395 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2396 {
2397   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2398   gboolean compatible_target = FALSE;
2399   GtkAction *action;
2400   gint i;
2401
2402   for (i = 0; i < G_N_ELEMENTS (targets); i++)
2403     {
2404       GdkAtom atom = gdk_atom_intern (targets[i].target, TRUE);
2405       if (gtk_clipboard_wait_is_target_available (clip, atom))
2406         {
2407           compatible_target = TRUE;
2408           break;
2409         }
2410     }
2411
2412   action = get_action_assert (data_sheet->builder, "edit_paste");
2413   gtk_action_set_sensitive (action, compatible_target);
2414 }