Changed the signature of the VARIABLE_CHANGED signal to take the WHAT and OLDVAR...
[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   column = psppire_data_sheet_find_column_for_variable (data_sheet,
1926                                                         dict_index);
1927   if (column == NULL)
1928     return;
1929
1930   var = psppire_dict_get_variable (data_store->dict, dict_index);
1931   g_return_if_fail (var != NULL);
1932
1933   name = escape_underscores (var_get_name (var));
1934   if (strcmp (name, pspp_sheet_view_column_get_title (column)))
1935     pspp_sheet_view_column_set_title (column, name);
1936   free (name);
1937
1938   cells = pspp_sheet_view_column_get_cell_renderers (column);
1939   g_return_if_fail (cells);
1940   cell = cells->data;
1941   g_list_free (cells);
1942
1943   if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
1944     {
1945       /* Stop editing before we delete and replace the cell renderers.
1946          Otherwise if this column is currently being edited, an eventual call
1947          to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
1948          that to gtk_cell_renderer_stop_editing(), which causes a critical.
1949
1950          It's possible that this is a bug in PsppSheetView, and it's possible
1951          that PsppSheetView inherits that from GtkTreeView, but I haven't
1952          investigated yet. */
1953       pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
1954
1955       add_data_column_cell_renderer (data_sheet, column);
1956     }
1957 }
1958
1959 static void
1960 on_variable_inserted (PsppireDict *dict, int var_index,
1961                       PsppireDataSheet *data_sheet)
1962 {
1963   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1964   gint base_width, incr_width;
1965   PsppSheetViewColumn *column;
1966
1967   calc_width_conversion (data_sheet, &base_width, &incr_width);
1968   column = make_data_column (data_sheet, var_index, base_width, incr_width);
1969   pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
1970 }
1971
1972 static void
1973 on_variable_deleted (PsppireDict *dict,
1974                      const struct variable *var, int case_idx, int width,
1975                      PsppireDataSheet *data_sheet)
1976 {
1977   PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
1978   GList *columns, *iter;
1979
1980   columns = pspp_sheet_view_get_columns (sheet_view);
1981   for (iter = columns; iter != NULL; iter = iter->next)
1982     {
1983       PsppSheetViewColumn *column = iter->data;
1984       const struct variable *column_var;
1985
1986       column_var = g_object_get_data (G_OBJECT (column), "variable");
1987       if (column_var == var)
1988         pspp_sheet_view_remove_column (sheet_view, column);
1989     }
1990   g_list_free (columns);
1991 }
1992
1993 static void
1994 psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
1995 {
1996   PsppireDataStore *store = data_sheet->data_store;
1997
1998   if (store == NULL)
1999     return;
2000
2001   data_sheet->data_store = NULL;
2002
2003   g_signal_handlers_disconnect_by_func (
2004     store, G_CALLBACK (on_backend_changed), data_sheet);
2005   g_signal_handlers_disconnect_by_func (
2006     store, G_CALLBACK (on_case_inserted), data_sheet);
2007   g_signal_handlers_disconnect_by_func (
2008     store, G_CALLBACK (on_cases_deleted), data_sheet);
2009   g_signal_handlers_disconnect_by_func (
2010     store, G_CALLBACK (on_case_change), data_sheet);
2011
2012   g_signal_handlers_disconnect_by_func (
2013     store->dict, G_CALLBACK (on_variable_changed), data_sheet);
2014   g_signal_handlers_disconnect_by_func (
2015     store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
2016   g_signal_handlers_disconnect_by_func (
2017     store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
2018   g_signal_handlers_disconnect_by_func (
2019     store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
2020
2021   g_object_unref (store);
2022 }
2023
2024 void
2025 psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
2026                                 PsppireDataStore *data_store)
2027 {
2028   psppire_data_sheet_unset_data_store (data_sheet);
2029
2030   data_sheet->data_store = data_store;
2031   if (data_store != NULL)
2032     {
2033       g_object_ref (data_store);
2034       g_signal_connect (data_store, "backend-changed",
2035                         G_CALLBACK (on_backend_changed), data_sheet);
2036       g_signal_connect (data_store, "case-inserted",
2037                         G_CALLBACK (on_case_inserted), data_sheet);
2038       g_signal_connect (data_store, "cases-deleted",
2039                         G_CALLBACK (on_cases_deleted), data_sheet);
2040       g_signal_connect (data_store, "case-changed",
2041                         G_CALLBACK (on_case_change), data_sheet);
2042
2043       /* XXX it's unclean to hook into the dict this way--what if the dict
2044          changes?  As of this writing, though, nothing ever changes the
2045          data_store's dict. */
2046       g_signal_connect (data_store->dict, "variable-changed",
2047                         G_CALLBACK (on_variable_changed),
2048                         data_sheet);
2049       g_signal_connect (data_store->dict, "variable-display-width-changed",
2050                         G_CALLBACK (on_variable_display_width_changed),
2051                         data_sheet);
2052       g_signal_connect (data_store->dict, "variable-inserted",
2053                         G_CALLBACK (on_variable_inserted), data_sheet);
2054       g_signal_connect (data_store->dict, "variable-deleted",
2055                         G_CALLBACK (on_variable_deleted), data_sheet);
2056     }
2057   refresh_model (data_sheet);
2058 }
2059 \f
2060 /* Clipboard stuff */
2061
2062 /* A casereader and dictionary holding the data currently in the clip */
2063 static struct casereader *clip_datasheet = NULL;
2064 static struct dictionary *clip_dict = NULL;
2065
2066
2067 static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
2068
2069 static gboolean
2070 psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
2071                                struct casereader **readerp,
2072                                struct dictionary **dictp)
2073 {
2074   struct casewriter *writer ;
2075   PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
2076   struct case_map *map = NULL;
2077   struct range_set *rows, *cols;
2078   const struct range_set_node *node;
2079   struct dictionary *dict;
2080
2081   if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2082     {
2083       *readerp = NULL;
2084       *dictp = NULL;
2085       return FALSE;
2086     }
2087
2088   /* Construct clip dictionary. */
2089   *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
2090   RANGE_SET_FOR_EACH (node, cols)
2091     {
2092       int dict_index;
2093
2094       for (dict_index = node->start; dict_index < node->end; dict_index++)
2095         {
2096           struct variable *var = dict_get_var (ds->dict->dict, dict_index);
2097           dict_clone_var_assert (dict, var);
2098         }
2099     }
2100
2101   /* Construct clip data. */
2102   map = case_map_by_name (ds->dict->dict, dict);
2103   writer = autopaging_writer_create (dict_get_proto (dict));
2104   RANGE_SET_FOR_EACH (node, rows)
2105     {
2106       unsigned long int row;
2107
2108       for (row = node->start; row < node->end; row++)
2109         {
2110           struct ccase *old = psppire_data_store_get_case (ds, row);
2111           if (old != NULL)
2112             casewriter_write (writer, case_map_execute (map, old));
2113           else
2114             casewriter_force_error (writer);
2115         }
2116     }
2117   case_map_destroy (map);
2118
2119   /* Clear data that we copied out, if we're doing a "cut". */
2120   if (cut && !casewriter_error (writer))
2121     {
2122       RANGE_SET_FOR_EACH (node, rows)
2123         {
2124           unsigned long int row;
2125
2126           for (row = node->start; row < node->end; row++)
2127             {
2128               const struct range_set_node *node2;
2129
2130               RANGE_SET_FOR_EACH (node2, cols)
2131                 {
2132                   int dict_index;
2133
2134                   for (dict_index = node2->start; dict_index < node2->end;
2135                        dict_index++)
2136                     {
2137                       struct variable *var;
2138
2139                       var = dict_get_var (ds->dict->dict, dict_index);
2140                       psppire_data_store_set_string (ds, "", row,
2141                                                      var, false);
2142                     }
2143                 }
2144             }
2145         }
2146     }
2147
2148   range_set_destroy (rows);
2149   range_set_destroy (cols);
2150
2151   *readerp = casewriter_make_reader (writer);
2152
2153   return TRUE;
2154 }
2155
2156 /* Set the clip from the currently selected range in DATA_SHEET.  If CUT is
2157    true, clears the original data from DATA_SHEET, otherwise leaves the
2158    original data in-place. */
2159 static void
2160 psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
2161                              gboolean cut)
2162 {
2163   struct casereader *reader;
2164   struct dictionary *dict;
2165
2166   if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
2167     {
2168       casereader_destroy (clip_datasheet);
2169       dict_destroy (clip_dict);
2170
2171       clip_datasheet = reader;
2172       clip_dict = dict;
2173
2174       psppire_data_sheet_update_clipboard (data_sheet);
2175     }
2176 }
2177
2178 enum {
2179   SELECT_FMT_NULL,
2180   SELECT_FMT_TEXT,
2181   SELECT_FMT_HTML
2182 };
2183
2184
2185 /* Perform data_out for case CC, variable V, appending to STRING */
2186 static void
2187 data_out_g_string (GString *string, const struct variable *v,
2188                    const struct ccase *cc)
2189 {
2190   const struct fmt_spec *fs = var_get_print_format (v);
2191   const union value *val = case_data (cc, v);
2192
2193   char *s = data_out (val, var_get_encoding (v), fs);
2194
2195   g_string_append (string, s);
2196
2197   g_free (s);
2198 }
2199
2200 static GString *
2201 clip_to_text (struct casereader *datasheet, struct dictionary *dict)
2202 {
2203   casenumber r;
2204   GString *string;
2205
2206   const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2207   const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2208   const size_t var_cnt = dict_get_var_cnt (dict);
2209
2210   string = g_string_sized_new (10 * val_cnt * case_cnt);
2211
2212   for (r = 0 ; r < case_cnt ; ++r )
2213     {
2214       int c;
2215       struct ccase *cc;
2216
2217       cc = casereader_peek (datasheet, r);
2218       if (cc == NULL)
2219         {
2220           g_warning ("Clipboard seems to have inexplicably shrunk");
2221           break;
2222         }
2223
2224       for (c = 0 ; c < var_cnt ; ++c)
2225         {
2226           const struct variable *v = dict_get_var (dict, c);
2227           data_out_g_string (string, v, cc);
2228           if ( c < val_cnt - 1 )
2229             g_string_append (string, "\t");
2230         }
2231
2232       if ( r < case_cnt)
2233         g_string_append (string, "\n");
2234
2235       case_unref (cc);
2236     }
2237
2238   return string;
2239 }
2240
2241
2242 static GString *
2243 clip_to_html (struct casereader *datasheet, struct dictionary *dict)
2244 {
2245   casenumber r;
2246   GString *string;
2247
2248   const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
2249   const casenumber case_cnt = casereader_get_case_cnt (datasheet);
2250   const size_t var_cnt = dict_get_var_cnt (dict);
2251
2252   /* Guestimate the size needed */
2253   string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
2254
2255   g_string_append (string,
2256                    "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
2257
2258   g_string_append (string, "<table>\n");
2259   for (r = 0 ; r < case_cnt ; ++r )
2260     {
2261       int c;
2262       struct ccase *cc = casereader_peek (datasheet, r);
2263       if (cc == NULL)
2264         {
2265           g_warning ("Clipboard seems to have inexplicably shrunk");
2266           break;
2267         }
2268       g_string_append (string, "<tr>\n");
2269
2270       for (c = 0 ; c < var_cnt ; ++c)
2271         {
2272           const struct variable *v = dict_get_var (dict, c);
2273           g_string_append (string, "<td>");
2274           data_out_g_string (string, v, cc);
2275           g_string_append (string, "</td>\n");
2276         }
2277
2278       g_string_append (string, "</tr>\n");
2279
2280       case_unref (cc);
2281     }
2282   g_string_append (string, "</table>\n");
2283
2284   return string;
2285 }
2286
2287
2288
2289 static void
2290 psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
2291                                   guint             info,
2292                                   struct casereader *reader,
2293                                   struct dictionary *dict)
2294 {
2295   GString *string = NULL;
2296
2297   switch (info)
2298     {
2299     case SELECT_FMT_TEXT:
2300       string = clip_to_text (reader, dict);
2301       break;
2302     case SELECT_FMT_HTML:
2303       string = clip_to_html (reader, dict);
2304       break;
2305     default:
2306       g_assert_not_reached ();
2307     }
2308
2309   gtk_selection_data_set (selection_data, selection_data->target,
2310                           8,
2311                           (const guchar *) string->str, string->len);
2312
2313   g_string_free (string, TRUE);
2314 }
2315
2316 static void
2317 psppire_data_sheet_clipboard_get_cb (GtkClipboard     *clipboard,
2318                                      GtkSelectionData *selection_data,
2319                                      guint             info,
2320                                      gpointer          data)
2321 {
2322   psppire_data_sheet_clipboard_set (selection_data, info,
2323                                     clip_datasheet, clip_dict);
2324 }
2325
2326 static void
2327 psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
2328                                        gpointer data)
2329 {
2330   dict_destroy (clip_dict);
2331   clip_dict = NULL;
2332
2333   casereader_destroy (clip_datasheet);
2334   clip_datasheet = NULL;
2335 }
2336
2337
2338 static const GtkTargetEntry targets[] = {
2339   { "UTF8_STRING",   0, SELECT_FMT_TEXT },
2340   { "STRING",        0, SELECT_FMT_TEXT },
2341   { "TEXT",          0, SELECT_FMT_TEXT },
2342   { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
2343   { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
2344   { "text/plain",    0, SELECT_FMT_TEXT },
2345   { "text/html",     0, SELECT_FMT_HTML }
2346 };
2347
2348
2349
2350 static void
2351 psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
2352 {
2353   GtkClipboard *clipboard =
2354     gtk_widget_get_clipboard (GTK_WIDGET (sheet),
2355                               GDK_SELECTION_CLIPBOARD);
2356
2357   if (!gtk_clipboard_set_with_owner (clipboard, targets,
2358                                      G_N_ELEMENTS (targets),
2359                                      psppire_data_sheet_clipboard_get_cb,
2360                                      psppire_data_sheet_clipboard_clear_cb,
2361                                      G_OBJECT (sheet)))
2362     psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
2363 }
2364
2365 static void
2366 psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
2367 {
2368   struct range_set *rows, *cols;
2369   GtkAction *action;
2370   gboolean enable;
2371
2372   enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
2373   if (enable)
2374     {
2375       range_set_destroy (rows);
2376       range_set_destroy (cols);
2377     }
2378
2379   action = get_action_assert (data_sheet->builder, "edit_copy");
2380   gtk_action_set_sensitive (action, enable);
2381
2382   action = get_action_assert (data_sheet->builder, "edit_cut");
2383   gtk_action_set_sensitive (action, enable);
2384 }
2385
2386 static void
2387 psppire_data_sheet_primary_get_cb (GtkClipboard     *clipboard,
2388                                    GtkSelectionData *selection_data,
2389                                    guint             info,
2390                                    gpointer          data)
2391 {
2392   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2393   struct casereader *reader;
2394   struct dictionary *dict;
2395
2396   if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
2397     {
2398       psppire_data_sheet_clipboard_set (selection_data, info,
2399                                         reader, dict);
2400       casereader_destroy (reader);
2401       dict_destroy (dict);
2402     }
2403 }
2404
2405 static void
2406 psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
2407                                              gboolean should_own)
2408 {
2409   GtkClipboard *clipboard;
2410   GdkDisplay *display;
2411
2412   display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
2413   clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
2414   g_return_if_fail (clipboard != NULL);
2415
2416   if (data_sheet->owns_primary_selection && !should_own)
2417     {
2418       data_sheet->owns_primary_selection = FALSE;
2419       gtk_clipboard_clear (clipboard);
2420     }
2421   else if (should_own)
2422     data_sheet->owns_primary_selection =
2423       gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2424                                     psppire_data_sheet_primary_get_cb,
2425                                     NULL, G_OBJECT (data_sheet));
2426 }
2427 \f
2428 /* A callback for when the clipboard contents have been received. */
2429 static void
2430 psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
2431                                      GtkSelectionData *sd,
2432                                      gpointer data)
2433 {
2434   PsppireDataSheet *data_sheet = data;
2435   PsppireDataStore *store = data_sheet->data_store;
2436   struct range_set *rows, *cols;
2437   gint count = 0;
2438   gint next_row, next_column;
2439   gint first_column;
2440   char *c;
2441
2442   if ( sd->length < 0 )
2443     return;
2444
2445   if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
2446     return;
2447
2448   c = (char *) sd->data;
2449
2450   /* Get the starting selected position in the data sheet.  (Possibly we should
2451      only paste into the selected range if it's larger than one cell?) */
2452   if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
2453     return;
2454   next_row = range_set_first (rows)->start;
2455   first_column = next_column = range_set_first (cols)->start;
2456   range_set_destroy (rows);
2457   range_set_destroy (cols);
2458
2459   g_return_if_fail (next_row >= 0);
2460   g_return_if_fail (next_column >= 0);
2461
2462   while (count < sd->length)
2463     {
2464       gint row = next_row;
2465       gint column = next_column;
2466       struct variable *var;
2467       char *s = c;
2468
2469       while (*c != '\t' && *c != '\n' && count < sd->length)
2470         {
2471           c++;
2472           count++;
2473         }
2474       if ( *c == '\t')
2475         {
2476           next_row = row ;
2477           next_column = column + 1;
2478         }
2479       else if ( *c == '\n')
2480         {
2481           next_row = row + 1;
2482           next_column = first_column;
2483         }
2484       *c++ = '\0';
2485       count++;
2486
2487       var = psppire_dict_get_variable (store->dict, column);
2488       if (var != NULL)
2489         psppire_data_store_set_string (store, s, row, var, FALSE);
2490     }
2491 }
2492
2493 static void
2494 psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
2495                                         GdkAtom *atoms,
2496                                         gint n_atoms,
2497                                         gpointer data)
2498 {
2499   GtkAction *action = GTK_ACTION (data);
2500   gboolean compatible_target;
2501   gint i;
2502
2503   compatible_target = FALSE;
2504   for (i = 0; i < G_N_ELEMENTS (targets); i++)
2505     {
2506       GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
2507       gint j;
2508
2509       for (j = 0; j < n_atoms; j++)
2510         if (target == atoms[j])
2511           {
2512             compatible_target = TRUE;
2513             break;
2514           }
2515     }
2516
2517   gtk_action_set_sensitive (action, compatible_target);
2518   g_object_unref (action);
2519 }
2520
2521 static void
2522 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
2523 {
2524   PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
2525   GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste");
2526
2527   g_object_ref (action);
2528   gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb,
2529                                  action);
2530 }