Merge 'psppsheet' into 'master'.
[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->clip != NULL && data_sheet->on_owner_change_signal != 0)
1207     {
1208       g_signal_handler_disconnect (data_sheet->clip,
1209                                    data_sheet->on_owner_change_signal);
1210       data_sheet->on_owner_change_signal = 0;
1211     }
1212
1213   if (data_sheet->dispose_has_run)
1214     return;
1215
1216   data_sheet->dispose_has_run = TRUE;
1217
1218   psppire_data_sheet_unset_data_store (data_sheet);
1219
1220   g_object_unref (data_sheet->builder);
1221
1222   if (data_sheet->uim)
1223     g_object_unref (data_sheet->uim);
1224
1225   G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object);
1226 }
1227
1228 static void
1229 psppire_data_sheet_map (GtkWidget *widget)
1230 {
1231   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1232
1233   GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget);
1234
1235   data_sheet->clip = gtk_widget_get_clipboard (widget,
1236                                                GDK_SELECTION_CLIPBOARD);
1237   if (data_sheet->on_owner_change_signal)
1238     g_signal_handler_disconnect (data_sheet->clip,
1239                                  data_sheet->on_owner_change_signal);
1240   data_sheet->on_owner_change_signal
1241     = g_signal_connect (data_sheet->clip, "owner-change",
1242                         G_CALLBACK (on_owner_change), widget);
1243   on_owner_change (data_sheet->clip, NULL, widget);
1244 }
1245
1246 static void
1247 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
1248 {
1249   GObjectClass *gobject_class;
1250   GtkWidgetClass *widget_class;
1251
1252   gobject_class = G_OBJECT_CLASS (class);
1253   gobject_class->set_property = psppire_data_sheet_set_property;
1254   gobject_class->get_property = psppire_data_sheet_get_property;
1255   gobject_class->dispose = psppire_data_sheet_dispose;
1256
1257   widget_class = GTK_WIDGET_CLASS (class);
1258   widget_class->map = psppire_data_sheet_map;
1259
1260   g_signal_new ("var-double-clicked",
1261                 G_OBJECT_CLASS_TYPE (gobject_class),
1262                 G_SIGNAL_RUN_LAST,
1263                 0,
1264                 g_signal_accumulator_true_handled, NULL,
1265                 psppire_marshal_BOOLEAN__INT,
1266                 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1267
1268   g_object_class_install_property (
1269     gobject_class, PROP_DATA_STORE,
1270     g_param_spec_object ("data-store",
1271                          "Data Store",
1272                          "The data store for the data sheet to display.",
1273                          PSPPIRE_TYPE_DATA_STORE,
1274                          G_PARAM_WRITABLE | G_PARAM_READABLE));
1275
1276   g_object_class_install_property (
1277     gobject_class, PROP_VALUE_LABELS,
1278     g_param_spec_boolean ("value-labels",
1279                           "Value Labels",
1280                           "Whether or not the data sheet should display labels instead of values",
1281                           FALSE,
1282                           G_PARAM_WRITABLE | G_PARAM_READABLE));
1283
1284   g_object_class_install_property (
1285     gobject_class, PROP_CASE_NUMBERS,
1286     g_param_spec_boolean ("case-numbers",
1287                           "Case Numbers",
1288                           "Whether or not the data sheet should display case numbers",
1289                           FALSE,
1290                           G_PARAM_WRITABLE | G_PARAM_READABLE));
1291
1292   g_object_class_install_property (
1293     gobject_class,
1294     PROP_CURRENT_CASE,
1295     g_param_spec_long ("current-case",
1296                        "Current Case",
1297                        "Zero based number of the selected case",
1298                        0, CASENUMBER_MAX,
1299                        0,
1300                        G_PARAM_WRITABLE | G_PARAM_READABLE));
1301
1302   g_object_class_install_property (
1303     gobject_class,
1304     PROP_MAY_CREATE_VARS,
1305     g_param_spec_boolean ("may-create-vars",
1306                           "May create variables",
1307                           "Whether the user may create more variables",
1308                           TRUE,
1309                           G_PARAM_READWRITE));
1310
1311   g_object_class_install_property (
1312     gobject_class,
1313     PROP_MAY_DELETE_VARS,
1314     g_param_spec_boolean ("may-delete-vars",
1315                           "May delete variables",
1316                           "Whether the user may delete variables",
1317                           TRUE,
1318                           G_PARAM_READWRITE));
1319
1320   g_object_class_install_property (
1321     gobject_class,
1322     PROP_UI_MANAGER,
1323     g_param_spec_object ("ui-manager",
1324                          "UI Manager",
1325                          "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.",
1326                          GTK_TYPE_UI_MANAGER,
1327                          G_PARAM_READABLE));
1328 }
1329
1330 static void
1331 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
1332 {
1333   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget);
1334   GtkWidget *menu;
1335
1336   menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup");
1337   gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
1338 }
1339
1340 static void
1341 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
1342 {
1343   do_popup_menu (widget, 0, gtk_get_current_event_time ());
1344 }
1345
1346 static gboolean
1347 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
1348                    gpointer user_data UNUSED)
1349 {
1350   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
1351
1352   if (event->type == GDK_BUTTON_PRESS && event->button == 3)
1353     {
1354       PsppSheetSelection *selection;
1355
1356       selection = pspp_sheet_view_get_selection (sheet_view);
1357       if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
1358         {
1359           GtkTreePath *path;
1360
1361           if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
1362                                                &path, NULL, NULL, NULL))
1363             {
1364               pspp_sheet_selection_unselect_all (selection);
1365               pspp_sheet_selection_select_path (selection, path);
1366               pspp_sheet_selection_select_all_columns (selection);
1367               gtk_tree_path_free (path);
1368             }
1369         }
1370
1371       do_popup_menu (widget, event->button, event->time);
1372
1373       return TRUE;
1374     }
1375
1376   return FALSE;
1377 }
1378
1379 static void
1380 on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet)
1381 {
1382   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1383   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1384   const struct range_set_node *node;
1385   struct range_set *selected;
1386
1387   selected = pspp_sheet_selection_get_range_set (selection);
1388   for (node = range_set_last (selected); node != NULL;
1389        node = range_set_prev (selected, node))
1390     {
1391       unsigned long int start = range_set_node_get_start (node);
1392       unsigned long int count = range_set_node_get_width (node);
1393
1394       psppire_data_store_delete_cases (data_sheet->data_store, start, count);
1395     }
1396   range_set_destroy (selected);
1397 }
1398
1399 static void
1400 on_selection_changed (PsppSheetSelection *selection,
1401                       gpointer user_data UNUSED)
1402 {
1403   PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1404   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view);
1405   gint n_selected_rows;
1406   gboolean any_variables_selected;
1407   gboolean may_delete_cases, may_delete_vars, may_insert_vars;
1408   GList *list, *iter;
1409   GtkTreePath *path;
1410   GtkAction *action;
1411
1412   n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1413
1414   action = get_action_assert (data_sheet->builder, "edit_insert-case");
1415   gtk_action_set_sensitive (action, n_selected_rows > 0);
1416
1417   switch (n_selected_rows)
1418     {
1419     case 0:
1420       may_delete_cases = FALSE;
1421       break;
1422
1423     case 1:
1424       /* The row used for inserting new cases cannot be deleted. */
1425       path = gtk_tree_path_new_from_indices (
1426         psppire_data_store_get_case_count (data_sheet->data_store), -1);
1427       may_delete_cases = !pspp_sheet_selection_path_is_selected (selection,
1428                                                                  path);
1429       gtk_tree_path_free (path);
1430       break;
1431
1432     default:
1433       may_delete_cases = TRUE;
1434       break;
1435     }
1436   action = get_action_assert (data_sheet->builder, "edit_clear-cases");
1437   gtk_action_set_sensitive (action, may_delete_cases);
1438
1439   any_variables_selected = FALSE;
1440   may_delete_vars = may_insert_vars = FALSE;
1441   list = pspp_sheet_selection_get_selected_columns (selection);
1442   for (iter = list; iter != NULL; iter = iter->next)
1443     {
1444       PsppSheetViewColumn *column = iter->data;
1445       struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1446
1447       if (var != NULL)
1448         {
1449           may_delete_vars = may_insert_vars = TRUE;
1450           any_variables_selected = TRUE;
1451           break;
1452         }
1453       if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL)
1454         may_insert_vars = TRUE;
1455     }
1456   g_list_free (list);
1457
1458   may_insert_vars = may_insert_vars && data_sheet->may_create_vars;
1459   may_delete_vars = may_delete_vars && data_sheet->may_delete_vars;
1460
1461   action = get_action_assert (data_sheet->builder, "edit_insert-variable");
1462   gtk_action_set_sensitive (action, may_insert_vars);
1463
1464   action = get_action_assert (data_sheet->builder, "edit_clear-variables");
1465   gtk_action_set_sensitive (action, may_delete_vars);
1466
1467   action = get_action_assert (data_sheet->builder, "sort-up");
1468   gtk_action_set_sensitive (action, may_delete_vars);
1469
1470   action = get_action_assert (data_sheet->builder, "sort-down");
1471   gtk_action_set_sensitive (action, may_delete_vars);
1472
1473   psppire_data_sheet_update_clip_actions (data_sheet);
1474   psppire_data_sheet_update_primary_selection (data_sheet,
1475                                                (n_selected_rows > 0
1476                                                 && any_variables_selected));
1477 }
1478
1479 static gboolean
1480 psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet,
1481                                     struct range_set **rowsp,
1482                                     struct range_set **colsp)
1483 {
1484   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1485   PsppireDataStore *data_store = data_sheet->data_store;
1486   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1487   unsigned long n_cases;
1488   struct range_set *rows, *cols;
1489   GList *list, *iter;
1490
1491   if (data_store == NULL)
1492     return FALSE;
1493   n_cases = psppire_data_store_get_case_count (data_store);
1494
1495   rows = pspp_sheet_selection_get_range_set (selection);
1496   range_set_set0 (rows, n_cases, ULONG_MAX - n_cases);
1497   if (range_set_is_empty (rows))
1498     {
1499       range_set_destroy (rows);
1500       return FALSE;
1501     }
1502
1503   cols = range_set_create ();
1504   list = pspp_sheet_selection_get_selected_columns (selection);
1505   for (iter = list; iter != NULL; iter = iter->next)
1506     {
1507       PsppSheetViewColumn *column = iter->data;
1508       struct variable *var = g_object_get_data (G_OBJECT (column), "variable");
1509
1510       if (var != NULL)
1511         range_set_set1 (cols, var_get_dict_index (var), 1);
1512     }
1513   g_list_free (list);
1514   if (range_set_is_empty (cols))
1515     {
1516       range_set_destroy (rows);
1517       range_set_destroy (cols);
1518       return FALSE;
1519     }
1520
1521   *rowsp = rows;
1522   *colsp = cols;
1523   return TRUE;
1524 }
1525
1526 static void
1527 on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet)
1528 {
1529   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1530   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1531   PsppireDataStore *data_store = data_sheet->data_store;
1532   struct range_set *selected;
1533   unsigned long row;
1534
1535   selected = pspp_sheet_selection_get_range_set (selection);
1536   row = range_set_scan (selected, 0);
1537   range_set_destroy (selected);
1538
1539   if (row <= psppire_data_store_get_case_count (data_store))
1540     psppire_data_store_insert_new_case (data_store, row);
1541 }
1542
1543 static void
1544 on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet)
1545 {
1546   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1547   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1548   PsppireDict *dict = data_sheet->data_store->dict;
1549   PsppSheetViewColumn *column;
1550   struct variable *var;
1551   gchar name[64];
1552   GList *list;
1553   gint index;
1554
1555   list = pspp_sheet_selection_get_selected_columns (selection);
1556   if (list == NULL)
1557     return;
1558   column = list->data;
1559   g_list_free (list);
1560
1561   var = g_object_get_data (G_OBJECT (column), "variable");
1562   index = var ? var_get_dict_index (var) : psppire_dict_get_var_cnt (dict);
1563   if (psppire_dict_generate_name (dict, name, sizeof name))
1564     psppire_dict_insert_variable (dict, index, name);
1565 }
1566
1567 static void
1568 on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet)
1569 {
1570   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1571   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1572   PsppireDict *dict = data_sheet->data_store->dict;
1573   GList *list, *iter;
1574
1575   list = pspp_sheet_selection_get_selected_columns (selection);
1576   if (list == NULL)
1577     return;
1578   list = g_list_reverse (list);
1579   for (iter = list; iter; iter = iter->next)
1580     {
1581       PsppSheetViewColumn *column = iter->data;
1582       struct variable *var;
1583
1584       var = g_object_get_data (G_OBJECT (column), "variable");
1585       if (var != NULL)
1586         psppire_dict_delete_variables (dict, var_get_dict_index (var), 1);
1587     }
1588   g_list_free (list);
1589 }
1590
1591 enum sort_order
1592   {
1593     SORT_ASCEND,
1594     SORT_DESCEND
1595   };
1596
1597 static void
1598 do_sort (PsppireDataSheet *data_sheet, enum sort_order order)
1599 {
1600   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1601   PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1602   PsppireDataWindow *pdw;
1603   GList *list, *iter;
1604   GString *syntax;
1605   int n_vars;
1606
1607   pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1608   g_return_if_fail (pdw != NULL);
1609
1610   list = pspp_sheet_selection_get_selected_columns (selection);
1611
1612   syntax = g_string_new ("SORT CASES BY");
1613   n_vars = 0;
1614   for (iter = list; iter; iter = iter->next)
1615     {
1616       PsppSheetViewColumn *column = iter->data;
1617       struct variable *var;
1618
1619       var = g_object_get_data (G_OBJECT (column), "variable");
1620       if (var != NULL)
1621         {
1622           g_string_append_printf (syntax, " %s", var_get_name (var));
1623           n_vars++;
1624         }
1625     }
1626   if (n_vars > 0)
1627     {
1628       if (order == SORT_DESCEND)
1629         g_string_append (syntax, " (DOWN)");
1630       g_string_append_c (syntax, '.');
1631       execute_const_syntax_string (pdw, syntax->str);
1632     }
1633   g_string_free (syntax, TRUE);
1634 }
1635
1636 void
1637 on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet)
1638 {
1639   do_sort (data_sheet, SORT_ASCEND);
1640 }
1641
1642 void
1643 on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet)
1644 {
1645   do_sort (data_sheet, SORT_DESCEND);
1646 }
1647
1648 void
1649 on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet)
1650 {
1651   goto_case_dialog (data_sheet);
1652 }
1653
1654 void
1655 on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet)
1656 {
1657   PsppireDataWindow *pdw;
1658
1659   pdw = psppire_data_window_for_data_store (data_sheet->data_store);
1660   g_return_if_fail (pdw != NULL);
1661
1662   find_dialog (pdw);
1663 }
1664
1665 void
1666 on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet)
1667 {
1668   psppire_data_sheet_set_clip (data_sheet, FALSE);
1669 }
1670
1671 void
1672 on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet)
1673 {
1674   psppire_data_sheet_set_clip (data_sheet, TRUE);
1675 }
1676
1677 void
1678 on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet)
1679 {
1680   GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
1681   GtkClipboard *clipboard =
1682     gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1683
1684   gtk_clipboard_request_contents (clipboard,
1685                                   gdk_atom_intern ("UTF8_STRING", TRUE),
1686                                   psppire_data_sheet_clip_received_cb,
1687                                   data_sheet);
1688 }
1689
1690 static void
1691 psppire_data_sheet_init (PsppireDataSheet *obj)
1692 {
1693   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1694   GtkAction *action;
1695
1696   obj->show_value_labels = FALSE;
1697   obj->show_case_numbers = TRUE;
1698   obj->may_create_vars = TRUE;
1699   obj->may_delete_vars = TRUE;
1700
1701   obj->owns_primary_selection = FALSE;
1702
1703   obj->scroll_to_bottom_signal = 0;
1704   obj->scroll_to_right_signal = 0;
1705   obj->on_owner_change_signal = 0;
1706   obj->new_variable_column = NULL;
1707   obj->container = NULL;
1708
1709   obj->uim = NULL;
1710   obj->dispose_has_run = FALSE;
1711
1712   pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES);
1713
1714   g_signal_connect (obj, "notify::model",
1715                     G_CALLBACK (psppire_data_sheet_model_changed), NULL);
1716
1717   pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1718   pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1719                                  PSPP_SHEET_SELECTION_RECTANGLE);
1720
1721   g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, (void *) NULL);
1722   g_signal_connect (obj, "query-tooltip",
1723                     G_CALLBACK (on_query_tooltip), NULL);
1724   g_signal_connect (obj, "button-press-event",
1725                     G_CALLBACK (on_button_pressed), NULL);
1726   g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1727
1728   obj->builder = builder_new ("data-sheet.ui");
1729
1730   action = get_action_assert (obj->builder, "edit_clear-cases");
1731   g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases),
1732                     obj);
1733   gtk_action_set_sensitive (action, FALSE);
1734   g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1735                     "changed", G_CALLBACK (on_selection_changed), NULL);
1736
1737   action = get_action_assert (obj->builder, "edit_insert-case");
1738   g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case),
1739                     obj);
1740
1741   action = get_action_assert (obj->builder, "edit_insert-variable");
1742   g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1743                     obj);
1744
1745   action = get_action_assert (obj->builder, "edit_goto-case");
1746   g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case),
1747                     obj);
1748
1749   action = get_action_assert (obj->builder, "edit_copy");
1750   g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj);
1751
1752   action = get_action_assert (obj->builder, "edit_cut");
1753   g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj);
1754
1755   action = get_action_assert (obj->builder, "edit_paste");
1756   g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj);
1757
1758   action = get_action_assert (obj->builder, "edit_clear-variables");
1759   g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1760                     obj);
1761
1762   action = get_action_assert (obj->builder, "edit_find");
1763   g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj);
1764
1765   action = get_action_assert (obj->builder, "sort-up");
1766   g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj);
1767
1768   action = get_action_assert (obj->builder, "sort-down");
1769   g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj);
1770
1771 }
1772
1773 GtkWidget *
1774 psppire_data_sheet_new (void)
1775 {
1776   return g_object_new (PSPP_TYPE_DATA_SHEET, NULL);
1777 }
1778
1779 PsppireDataStore *
1780 psppire_data_sheet_get_data_store (PsppireDataSheet *data_sheet)
1781 {
1782   return data_sheet->data_store;
1783 }
1784
1785 static void
1786 refresh_model (PsppireDataSheet *data_sheet)
1787 {
1788   pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), NULL);
1789
1790   if (data_sheet->data_store != NULL)
1791     {
1792       PsppireEmptyListStore *model;
1793       GtkAction *action;
1794       int n_rows;
1795
1796       n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1;
1797       model = psppire_empty_list_store_new (n_rows);
1798       pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet),
1799                                  GTK_TREE_MODEL (model));
1800       g_object_unref (model);
1801
1802       action = get_action_assert (data_sheet->builder, "edit_copy");
1803       g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy),
1804                         data_sheet);
1805     }
1806 }
1807
1808 static void
1809 on_case_inserted (PsppireDataStore *data_store, gint row,
1810                   PsppireDataSheet *data_sheet)
1811 {
1812   PsppireEmptyListStore *empty_list_store;
1813   GtkTreeModel *tree_model;
1814   gint n_rows;
1815
1816   g_return_if_fail (data_store == data_sheet->data_store);
1817
1818   n_rows = psppire_data_store_get_case_count (data_store) + 1;
1819   if (row == n_rows - 1)
1820     row++;
1821
1822   tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1823   empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1824   psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1825   psppire_empty_list_store_row_inserted (empty_list_store, row);
1826 }
1827
1828 static void
1829 on_cases_deleted (PsppireDataStore *data_store, gint first, gint n_cases,
1830                   PsppireDataSheet *data_sheet)
1831 {
1832
1833   g_return_if_fail (data_store == data_sheet->data_store);
1834
1835   if (n_cases > 1)
1836     {
1837       /* This is a bit of a cop-out.  We could do better, if it ever turns out
1838          that this performs too poorly. */
1839       refresh_model (data_sheet);
1840     }
1841   else
1842     {
1843       PsppireEmptyListStore *empty_list_store;
1844       GtkTreeModel *tree_model;
1845       gint n_rows = psppire_data_store_get_case_count (data_store) + 1;
1846
1847       tree_model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (data_sheet));
1848       empty_list_store = PSPPIRE_EMPTY_LIST_STORE (tree_model);
1849       psppire_empty_list_store_set_n_rows (empty_list_store, n_rows);
1850       psppire_empty_list_store_row_deleted (empty_list_store, first);
1851     }
1852 }
1853
1854 static void
1855 on_case_change (PsppireDataStore *data_store, gint row,
1856                 PsppireDataSheet *data_sheet)
1857 {
1858   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1859
1860   pspp_sheet_view_stop_editing (sheet_view, TRUE);
1861   gtk_widget_queue_draw (GTK_WIDGET (data_sheet));
1862 }
1863
1864 static void
1865 on_backend_changed (PsppireDataStore *data_store,
1866                     PsppireDataSheet *data_sheet)
1867 {
1868   g_return_if_fail (data_store == data_sheet->data_store);
1869   refresh_model (data_sheet);
1870 }
1871
1872 static void
1873 on_variable_display_width_changed (PsppireDict *dict, int dict_index,
1874                                    PsppireDataSheet *data_sheet)
1875 {
1876   PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1877   PsppSheetViewColumn *column;
1878   struct variable *var;
1879   int display_width;
1880   gint pixel_width;
1881
1882   g_return_if_fail (data_sheet->data_store != NULL);
1883   g_return_if_fail (dict == data_sheet->data_store->dict);
1884
1885   column = psppire_data_sheet_find_column_for_variable (data_sheet,
1886                                                         dict_index);
1887   if (column == NULL)
1888     return;
1889
1890   var = psppire_dict_get_variable (data_store->dict, dict_index);
1891   g_return_if_fail (var != NULL);
1892
1893   pixel_width = pspp_sheet_view_column_get_fixed_width (column);
1894   display_width = display_width_from_pixel_width (data_sheet, pixel_width);
1895   if (display_width != var_get_display_width (var))
1896     {
1897       gint base_width, incr_width;
1898
1899       display_width = var_get_display_width (var);
1900       calc_width_conversion (data_sheet, &base_width, &incr_width);
1901       pixel_width = display_width_to_pixel_width (data_sheet, display_width,
1902                                                   base_width, incr_width);
1903       pspp_sheet_view_column_set_fixed_width (column, pixel_width);
1904     }
1905 }
1906
1907 static void
1908 on_variable_changed (PsppireDict *dict, int dict_index,
1909                      PsppireDataSheet *data_sheet)
1910 {
1911   PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
1912   PsppSheetViewColumn *column;
1913   GtkCellRenderer *cell;
1914   struct variable *var;
1915   GList *cells;
1916   char *name;
1917
1918   g_return_if_fail (data_sheet->data_store != NULL);
1919   g_return_if_fail (dict == data_sheet->data_store->dict);
1920
1921   column = psppire_data_sheet_find_column_for_variable (data_sheet,
1922                                                         dict_index);
1923   if (column == NULL)
1924     return;
1925
1926   var = psppire_dict_get_variable (data_store->dict, dict_index);
1927   g_return_if_fail (var != NULL);
1928
1929   name = escape_underscores (var_get_name (var));
1930   if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1931     pspp_sheet_view_column_set_title (column, name);
1932   free (name);
1933
1934   cells = pspp_sheet_view_column_get_cell_renderers (column);
1935   g_return_if_fail (cells);
1936   cell = cells->data;
1937   g_list_free (cells);
1938
1939   if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1940     {
1941       /* Stop editing before we delete and replace the cell renderers.
1942          Otherwise if this column is currently being edited, an eventual call
1943          to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1944          that to gtk_cell_renderer_stop_editing(), which causes a critical.
1945
1946          It's possible that this is a bug in PsppSheetView, and it's possible
1947          that PsppSheetView inherits that from GtkTreeView, but I haven't
1948          investigated yet. */
1949       pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1950
1951       add_data_column_cell_renderer (data_sheet, column);
1952     }
1953 }
1954
1955 static void
1956 on_variable_inserted (PsppireDict *dict, int var_index,
1957                       PsppireDataSheet *data_sheet)
1958 {
1959   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1960   gint base_width, incr_width;
1961   PsppSheetViewColumn *column;
1962
1963   calc_width_conversion (data_sheet, &base_width, &incr_width);
1964   column = make_data_column (data_sheet, var_index, base_width, incr_width);
1965   pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1966 }
1967
1968 static void
1969 on_variable_deleted (PsppireDict *dict,
1970                      const struct variable *var, int case_idx, int width,
1971                      PsppireDataSheet *data_sheet)
1972 {
1973   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1974   GList *columns, *iter;
1975
1976   columns = pspp_sheet_view_get_columns (sheet_view);
1977   for (iter = columns; iter != NULL; iter = iter->next)
1978     {
1979       PsppSheetViewColumn *column = iter->data;
1980       const struct variable *column_var;
1981
1982       column_var = g_object_get_data (G_OBJECT (column), "variable");
1983       if (column_var == var)
1984         pspp_sheet_view_remove_column (sheet_view, column);
1985     }
1986   g_list_free (columns);
1987 }
1988
1989 static void
1990 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1991 {
1992   PsppireDataStore *store = data_sheet->data_store;
1993
1994   if (store == NULL)
1995     return;
1996
1997   data_sheet->data_store = NULL;
1998
1999   g_signal_handlers_disconnect_by_func (
2000     store, G_CALLBACK (on_backend_changed), data_sheet);
2001   g_signal_handlers_disconnect_by_func (
2002     store, G_CALLBACK (on_case_inserted), data_sheet);
2003   g_signal_handlers_disconnect_by_func (
2004     store, G_CALLBACK (on_cases_deleted), data_sheet);
2005   g_signal_handlers_disconnect_by_func (
2006     store, G_CALLBACK (on_case_change), data_sheet);
2007
2008   g_signal_handlers_disconnect_by_func (
2009     store->dict, G_CALLBACK (on_variable_changed), data_sheet);
2010   g_signal_handlers_disconnect_by_func (
2011     store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
2012   g_signal_handlers_disconnect_by_func (
2013     store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
2014   g_signal_handlers_disconnect_by_func (
2015     store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
2016
2017   g_object_unref (store);
2018 }
2019
2020 void
2021 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
2022                                 PsppireDataStore *data_store)
2023 {
2024   psppire_data_sheet_unset_data_store (data_sheet);
2025
2026   data_sheet->data_store = data_store;
2027   if (data_store != NULL)
2028     {
2029       g_object_ref (data_store);
2030       g_signal_connect (data_store, "backend-changed",
2031                         G_CALLBACK (on_backend_changed), data_sheet);
2032       g_signal_connect (data_store, "case-inserted",
2033                         G_CALLBACK (on_case_inserted), data_sheet);
2034       g_signal_connect (data_store, "cases-deleted",
2035                         G_CALLBACK (on_cases_deleted), data_sheet);
2036       g_signal_connect (data_store, "case-changed",
2037                         G_CALLBACK (on_case_change), data_sheet);
2038
2039       /* XXX it's unclean to hook into the dict this way--what if the dict
2040          changes?  As of this writing, though, nothing ever changes the
2041          data_store's dict. */
2042       g_signal_connect (data_store->dict, "variable-changed",
2043                         G_CALLBACK (on_variable_changed),
2044                         data_sheet);
2045       g_signal_connect (data_store->dict, "variable-display-width-changed",
2046                         G_CALLBACK (on_variable_display_width_changed),
2047                         data_sheet);
2048       g_signal_connect (data_store->dict, "variable-inserted",
2049                         G_CALLBACK (on_variable_inserted), data_sheet);
2050       g_signal_connect (data_store->dict, "variable-deleted",
2051                         G_CALLBACK (on_variable_deleted), data_sheet);
2052     }
2053   refresh_model (data_sheet);
2054 }
2055 \f
2056 /* Clipboard stuff */
2057
2058 /* A casereader and dictionary holding the data currently in the clip */
2059 static struct casereader *clip_datasheet = NULL;
2060 static struct dictionary *clip_dict = NULL;
2061
2062
2063 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2064
2065 static gboolean
2066 psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
2067                                struct casereader **readerp,
2068                                struct dictionary **dictp)
2069 {
2070   struct casewriter *writer ;
2071   PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2072   struct case_map *map = NULL;
2073   struct range_set *rows, *cols;
2074   const struct range_set_node *node;
2075   struct dictionary *dict;
2076
2077   if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2078     {
2079       *readerp = NULL;
2080       *dictp = NULL;
2081       return FALSE;
2082     }
2083
2084   /* Construct clip dictionary. */
2085   *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
2086   RANGE_SET_FOR_EACH (node, cols)
2087     {
2088       int dict_index;
2089
2090       for (dict_index = node->start; dict_index < node->end; dict_index++)
2091         {
2092           struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2093           dict_clone_var_assert (dict, var);
2094         }
2095     }
2096
2097   /* Construct clip data. */
2098   map = case_map_by_name (ds->dict->dict, dict);
2099   writer = autopaging_writer_create (dict_get_proto (dict));
2100   RANGE_SET_FOR_EACH (node, rows)
2101     {
2102       unsigned long int row;
2103
2104       for (row = node->start; row < node->end; row++)
2105         {
2106           struct ccase *old = psppire_data_store_get_case (ds, row);
2107           if (old != NULL)
2108             casewriter_write (writer, case_map_execute (map, old));
2109           else
2110             casewriter_force_error (writer);
2111         }
2112     }
2113   case_map_destroy (map);
2114
2115   /* Clear data that we copied out, if we're doing a "cut". */
2116   if (cut && !casewriter_error (writer))
2117     {
2118       RANGE_SET_FOR_EACH (node, rows)
2119         {
2120           unsigned long int row;
2121
2122           for (row = node->start; row < node->end; row++)
2123             {
2124               const struct range_set_node *node2;
2125
2126               RANGE_SET_FOR_EACH (node2, cols)
2127                 {
2128                   int dict_index;
2129
2130                   for (dict_index = node2->start; dict_index < node2->end;
2131                        dict_index++)
2132                     {
2133                       struct variable *var;
2134
2135                       var = dict_get_var (ds->dict->dict, dict_index);
2136                       psppire_data_store_set_string (ds, "", row,
2137                                                      var, false);
2138                     }
2139                 }
2140             }
2141         }
2142     }
2143
2144   range_set_destroy (rows);
2145   range_set_destroy (cols);
2146
2147   *readerp = casewriter_make_reader (writer);
2148
2149   return TRUE;
2150 }
2151
2152 /* Set the clip from the currently selected range in DATA_SHEET.  If CUT is
2153    true, clears the original data from DATA_SHEET, otherwise leaves the
2154    original data in-place. */
2155 static void
2156 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2157                              gboolean cut)
2158 {
2159   struct casereader *reader;
2160   struct dictionary *dict;
2161
2162   if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
2163     {
2164       casereader_destroy (clip_datasheet);
2165       dict_destroy (clip_dict);
2166
2167       clip_datasheet = reader;
2168       clip_dict = dict;
2169
2170       psppire_data_sheet_update_clipboard (data_sheet);
2171     }
2172 }
2173
2174 enum {
2175   SELECT_FMT_NULL,
2176   SELECT_FMT_TEXT,
2177   SELECT_FMT_HTML
2178 };
2179
2180
2181 /* Perform data_out for case CC, variable V, appending to STRING */
2182 static void
2183 data_out_g_string (GString *string, const struct variable *v,
2184                    const struct ccase *cc)
2185 {
2186   const struct fmt_spec *fs = var_get_print_format (v);
2187   const union value *val = case_data (cc, v);
2188
2189   char *s = data_out (val, var_get_encoding (v), fs);
2190
2191   g_string_append (string, s);
2192
2193   g_free (s);
2194 }
2195
2196 static GString *
2197 clip_to_text (struct casereader *datasheet, struct dictionary *dict)
2198 {
2199   casenumber r;
2200   GString *string;
2201
2202   const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2203   const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2204   const size_t var_cnt = dict_get_var_cnt (dict);
2205
2206   string = g_string_sized_new (10 * val_cnt * case_cnt);
2207
2208   for (r = 0 ; r < case_cnt ; ++r )
2209     {
2210       int c;
2211       struct ccase *cc;
2212
2213       cc = casereader_peek (datasheet, r);
2214       if (cc == NULL)
2215         {
2216           g_warning ("Clipboard seems to have inexplicably shrunk");
2217           break;
2218         }
2219
2220       for (c = 0 ; c < var_cnt ; ++c)
2221         {
2222           const struct variable *v = dict_get_var (dict, c);
2223           data_out_g_string (string, v, cc);
2224           if ( c < val_cnt - 1 )
2225             g_string_append (string, "\t");
2226         }
2227
2228       if ( r < case_cnt)
2229         g_string_append (string, "\n");
2230
2231       case_unref (cc);
2232     }
2233
2234   return string;
2235 }
2236
2237
2238 static GString *
2239 clip_to_html (struct casereader *datasheet, struct dictionary *dict)
2240 {
2241   casenumber r;
2242   GString *string;
2243
2244   const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2245   const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2246   const size_t var_cnt = dict_get_var_cnt (dict);
2247
2248   /* Guestimate the size needed */
2249   string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2250
2251   g_string_append (string,
2252                    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2253
2254   g_string_append (string, "<table>\n");
2255   for (r = 0 ; r < case_cnt ; ++r )
2256     {
2257       int c;
2258       struct ccase *cc = casereader_peek (datasheet, r);
2259       if (cc == NULL)
2260         {
2261           g_warning ("Clipboard seems to have inexplicably shrunk");
2262           break;
2263         }
2264       g_string_append (string, "<tr>\n");
2265
2266       for (c = 0 ; c < var_cnt ; ++c)
2267         {
2268           const struct variable *v = dict_get_var (dict, c);
2269           g_string_append (string, "<td>");
2270           data_out_g_string (string, v, cc);
2271           g_string_append (string, "</td>\n");
2272         }
2273
2274       g_string_append (string, "</tr>\n");
2275
2276       case_unref (cc);
2277     }
2278   g_string_append (string, "</table>\n");
2279
2280   return string;
2281 }
2282
2283
2284
2285 static void
2286 psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
2287                                   guint             info,
2288                                   struct casereader *reader,
2289                                   struct dictionary *dict)
2290 {
2291   GString *string = NULL;
2292
2293   switch (info)
2294     {
2295     case SELECT_FMT_TEXT:
2296       string = clip_to_text (reader, dict);
2297       break;
2298     case SELECT_FMT_HTML:
2299       string = clip_to_html (reader, dict);
2300       break;
2301     default:
2302       g_assert_not_reached ();
2303     }
2304
2305   gtk_selection_data_set (selection_data, selection_data->target,
2306                           8,
2307                           (const guchar *) string->str, string->len);
2308
2309   g_string_free (string, TRUE);
2310 }
2311
2312 static void
2313 psppire_data_sheet_clipboard_get_cb (GtkClipboard     *clipboard,
2314                                      GtkSelectionData *selection_data,
2315                                      guint             info,
2316                                      gpointer          data)
2317 {
2318   psppire_data_sheet_clipboard_set (selection_data, info,
2319                                     clip_datasheet, clip_dict);
2320 }
2321
2322 static void
2323 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2324                                        gpointer data)
2325 {
2326   dict_destroy (clip_dict);
2327   clip_dict = NULL;
2328
2329   casereader_destroy (clip_datasheet);
2330   clip_datasheet = NULL;
2331 }
2332
2333
2334 static const GtkTargetEntry targets[] = {
2335   { "UTF8_STRING",   0, SELECT_FMT_TEXT },
2336   { "STRING",        0, SELECT_FMT_TEXT },
2337   { "TEXT",          0, SELECT_FMT_TEXT },
2338   { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2339   { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2340   { "text/plain",    0, SELECT_FMT_TEXT },
2341   { "text/html",     0, SELECT_FMT_HTML }
2342 };
2343
2344
2345
2346 static void
2347 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2348 {
2349   GtkClipboard *clipboard =
2350     gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2351                               GDK_SELECTION_CLIPBOARD);
2352
2353   if (!gtk_clipboard_set_with_owner (clipboard, targets,
2354                                      G_N_ELEMENTS (targets),
2355                                      psppire_data_sheet_clipboard_get_cb,
2356                                      psppire_data_sheet_clipboard_clear_cb,
2357                                      G_OBJECT (sheet)))
2358     psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2359 }
2360
2361 static void
2362 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2363 {
2364   struct range_set *rows, *cols;
2365   GtkAction *action;
2366   gboolean enable;
2367
2368   enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2369   if (enable)
2370     {
2371       range_set_destroy (rows);
2372       range_set_destroy (cols);
2373     }
2374
2375   action = get_action_assert (data_sheet->builder, "edit_copy");
2376   gtk_action_set_sensitive (action, enable);
2377
2378   action = get_action_assert (data_sheet->builder, "edit_cut");
2379   gtk_action_set_sensitive (action, enable);
2380 }
2381
2382 static void
2383 psppire_data_sheet_primary_get_cb (GtkClipboard     *clipboard,
2384                                    GtkSelectionData *selection_data,
2385                                    guint             info,
2386                                    gpointer          data)
2387 {
2388   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2389   struct casereader *reader;
2390   struct dictionary *dict;
2391
2392   if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
2393     {
2394       psppire_data_sheet_clipboard_set (selection_data, info,
2395                                         reader, dict);
2396       casereader_destroy (reader);
2397       dict_destroy (dict);
2398     }
2399 }
2400
2401 static void
2402 psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
2403                                              gboolean should_own)
2404 {
2405   GtkClipboard *clipboard;
2406   GdkDisplay *display;
2407
2408   display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
2409   clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
2410   g_return_if_fail (clipboard != NULL);
2411
2412   if (data_sheet->owns_primary_selection && !should_own)
2413     {
2414       data_sheet->owns_primary_selection = FALSE;
2415       gtk_clipboard_clear (clipboard);
2416     }
2417   else if (should_own)
2418     data_sheet->owns_primary_selection =
2419       gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2420                                     psppire_data_sheet_primary_get_cb,
2421                                     NULL, G_OBJECT (data_sheet));
2422 }
2423 \f
2424 /* A callback for when the clipboard contents have been received. */
2425 static void
2426 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2427                                      GtkSelectionData *sd,
2428                                      gpointer data)
2429 {
2430   PsppireDataSheet *data_sheet = data;
2431   PsppireDataStore *store = data_sheet->data_store;
2432   struct range_set *rows, *cols;
2433   gint count = 0;
2434   gint next_row, next_column;
2435   gint first_column;
2436   char *c;
2437
2438   if ( sd->length < 0 )
2439     return;
2440
2441   if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
2442     return;
2443
2444   c = (char *) sd->data;
2445
2446   /* Get the starting selected position in the data sheet.  (Possibly we should
2447      only paste into the selected range if it's larger than one cell?) */
2448   if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2449     return;
2450   next_row = range_set_first (rows)->start;
2451   first_column = next_column = range_set_first (cols)->start;
2452   range_set_destroy (rows);
2453   range_set_destroy (cols);
2454
2455   g_return_if_fail (next_row >= 0);
2456   g_return_if_fail (next_column >= 0);
2457
2458   while (count < sd->length)
2459     {
2460       gint row = next_row;
2461       gint column = next_column;
2462       struct variable *var;
2463       char *s = c;
2464
2465       while (*c != '\t' && *c != '\n' && count < sd->length)
2466         {
2467           c++;
2468           count++;
2469         }
2470       if ( *c == '\t')
2471         {
2472           next_row = row ;
2473           next_column = column + 1;
2474         }
2475       else if ( *c == '\n')
2476         {
2477           next_row = row + 1;
2478           next_column = first_column;
2479         }
2480       *c++ = '\0';
2481       count++;
2482
2483       var = psppire_dict_get_variable (store->dict, column);
2484       if (var != NULL)
2485         psppire_data_store_set_string (store, s, row, var, FALSE);
2486     }
2487 }
2488
2489 static void
2490 psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
2491                                         GdkAtom *atoms,
2492                                         gint n_atoms,
2493                                         gpointer data)
2494 {
2495   GtkAction *action = GTK_ACTION (data);
2496   gboolean compatible_target;
2497   gint i;
2498
2499   compatible_target = FALSE;
2500   for (i = 0; i < G_N_ELEMENTS (targets); i++)
2501     {
2502       GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
2503       gint j;
2504
2505       for (j = 0; j < n_atoms; j++)
2506         if (target == atoms[j])
2507           {
2508             compatible_target = TRUE;
2509             break;
2510           }
2511     }
2512
2513   gtk_action_set_sensitive (action, compatible_target);
2514   g_object_unref (action);
2515 }
2516
2517 static void
2518 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2519 {
2520   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2521   GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste");
2522
2523   g_object_ref (action);
2524   gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb,
2525                                  action);
2526 }