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