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