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