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