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