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