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