-static void
-on_variable_display_width_changed (PsppireDict *dict, int dict_index,
- PsppireDataSheet *data_sheet)
-{
- PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
- PsppSheetViewColumn *column;
- struct variable *var;
- int display_width;
- gint pixel_width;
-
- g_return_if_fail (data_sheet->data_store != NULL);
- g_return_if_fail (dict == data_sheet->data_store->dict);
-
- column = psppire_data_sheet_find_column_for_variable (data_sheet,
- dict_index);
- if (column == NULL)
- return;
-
- var = psppire_dict_get_variable (data_store->dict, dict_index);
- g_return_if_fail (var != NULL);
-
- pixel_width = pspp_sheet_view_column_get_fixed_width (column);
- display_width = display_width_from_pixel_width (data_sheet, pixel_width);
- if (display_width != var_get_display_width (var))
- {
- gint base_width, incr_width;
-
- display_width = var_get_display_width (var);
- calc_width_conversion (data_sheet, &base_width, &incr_width);
- pixel_width = display_width_to_pixel_width (data_sheet, display_width,
- base_width, incr_width);
- pspp_sheet_view_column_set_fixed_width (column, pixel_width);
- }
-}
-
-static void
-on_variable_changed (PsppireDict *dict, int dict_index,
- guint what, const struct variable *oldvar,
- PsppireDataSheet *data_sheet)
-{
- PsppireDataStore *data_store = psppire_data_sheet_get_data_store (data_sheet);
- PsppSheetViewColumn *column;
- GtkCellRenderer *cell;
- struct variable *var;
- GList *cells;
- char *name;
-
- g_return_if_fail (data_sheet->data_store != NULL);
- g_return_if_fail (dict == data_sheet->data_store->dict);
-
-
- if (what & VAR_TRAIT_DISPLAY_WIDTH)
- on_variable_display_width_changed (dict, dict_index, data_sheet);
-
- column = psppire_data_sheet_find_column_for_variable (data_sheet,
- dict_index);
- if (column == NULL)
- return;
-
-
- var = psppire_dict_get_variable (data_store->dict, dict_index);
- g_return_if_fail (var != NULL);
-
- name = escape_underscores (var_get_name (var));
- if (strcmp (name, pspp_sheet_view_column_get_title (column)))
- pspp_sheet_view_column_set_title (column, name);
- free (name);
-
- cells = pspp_sheet_view_column_get_cell_renderers (column);
- g_return_if_fail (cells);
- cell = cells->data;
- g_list_free (cells);
-
- if (var_has_value_labels (var) != GTK_IS_CELL_RENDERER_COMBO (cell))
- {
- /* Stop editing before we delete and replace the cell renderers.
- Otherwise if this column is currently being edited, an eventual call
- to pspp_sheet_view_stop_editing() will obtain a NULL cell and pass
- that to gtk_cell_renderer_stop_editing(), which causes a critical.
-
- It's possible that this is a bug in PsppSheetView, and it's possible
- that PsppSheetView inherits that from GtkTreeView, but I haven't
- investigated yet. */
- pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (data_sheet), TRUE);
-
- add_data_column_cell_renderer (data_sheet, column);
- }
-}
-
-static void
-on_variable_inserted (PsppireDict *dict, int var_index,
- PsppireDataSheet *data_sheet)
-{
- PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
- gint base_width, incr_width;
- PsppSheetViewColumn *column;
-
- calc_width_conversion (data_sheet, &base_width, &incr_width);
- column = make_data_column (data_sheet, var_index, base_width, incr_width);
- pspp_sheet_view_insert_column (sheet_view, column, var_index + 1);
-}
-
-static void
-on_variable_deleted (PsppireDict *dict,
- const struct variable *var, int case_idx, int width,
- PsppireDataSheet *data_sheet)
-{
- PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
- GList *columns, *iter;
-
- columns = pspp_sheet_view_get_columns (sheet_view);
- for (iter = columns; iter != NULL; iter = iter->next)
- {
- PsppSheetViewColumn *column = iter->data;
- const struct variable *column_var;
-
- column_var = g_object_get_data (G_OBJECT (column), "variable");
- if (column_var == var)
- pspp_sheet_view_remove_column (sheet_view, column);
- }
- g_list_free (columns);
-}
-
-static void
-psppire_data_sheet_unset_data_store (PsppireDataSheet *data_sheet)
-{
- PsppireDataStore *store = data_sheet->data_store;
-
- if (store == NULL)
- return;
-
- data_sheet->data_store = NULL;
-
- g_signal_handlers_disconnect_by_func (
- store, G_CALLBACK (on_backend_changed), data_sheet);
- g_signal_handlers_disconnect_by_func (
- store, G_CALLBACK (on_case_inserted), data_sheet);
- g_signal_handlers_disconnect_by_func (
- store, G_CALLBACK (on_cases_deleted), data_sheet);
- g_signal_handlers_disconnect_by_func (
- store, G_CALLBACK (on_case_change), data_sheet);
-
- g_signal_handlers_disconnect_by_func (
- store->dict, G_CALLBACK (on_variable_changed), data_sheet);
- g_signal_handlers_disconnect_by_func (
- store->dict, G_CALLBACK (on_variable_display_width_changed), data_sheet);
- g_signal_handlers_disconnect_by_func (
- store->dict, G_CALLBACK (on_variable_inserted), data_sheet);
- g_signal_handlers_disconnect_by_func (
- store->dict, G_CALLBACK (on_variable_deleted), data_sheet);
-
- g_object_unref (store);
-}
-
-void
-psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet,
- PsppireDataStore *data_store)
-{
- psppire_data_sheet_unset_data_store (data_sheet);
-
- data_sheet->data_store = data_store;
- if (data_store != NULL)
- {
- g_object_ref (data_store);
- g_signal_connect (data_store, "backend-changed",
- G_CALLBACK (on_backend_changed), data_sheet);
- g_signal_connect (data_store, "case-inserted",
- G_CALLBACK (on_case_inserted), data_sheet);
- g_signal_connect (data_store, "cases-deleted",
- G_CALLBACK (on_cases_deleted), data_sheet);
- g_signal_connect (data_store, "case-changed",
- G_CALLBACK (on_case_change), data_sheet);
-
- /* XXX it's unclean to hook into the dict this way--what if the dict
- changes? As of this writing, though, nothing ever changes the
- data_store's dict. */
- g_signal_connect (data_store->dict, "variable-changed",
- G_CALLBACK (on_variable_changed),
- data_sheet);
- g_signal_connect (data_store->dict, "variable-inserted",
- G_CALLBACK (on_variable_inserted), data_sheet);
- g_signal_connect (data_store->dict, "variable-deleted",
- G_CALLBACK (on_variable_deleted), data_sheet);
- }
- refresh_model (data_sheet);
-}
-\f
-/* Clipboard stuff */
-
-/* A casereader and dictionary holding the data currently in the clip */
-static struct casereader *clip_datasheet = NULL;
-static struct dictionary *clip_dict = NULL;
-
-
-static void psppire_data_sheet_update_clipboard (PsppireDataSheet *);
-
-static gboolean
-psppire_data_sheet_fetch_clip (PsppireDataSheet *data_sheet, gboolean cut,
- struct casereader **readerp,
- struct dictionary **dictp)
-{
- struct casewriter *writer ;
- PsppireDataStore *ds = psppire_data_sheet_get_data_store (data_sheet);
- struct case_map *map = NULL;
- struct range_set *rows, *cols;
- const struct range_set_node *node;
- struct dictionary *dict;
-
- if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
- {
- *readerp = NULL;
- *dictp = NULL;
- return FALSE;
- }
-
- /* Construct clip dictionary. */
- *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict));
- RANGE_SET_FOR_EACH (node, cols)
- {
- int dict_index;
-
- for (dict_index = node->start; dict_index < node->end; dict_index++)
- {
- struct variable *var = dict_get_var (ds->dict->dict, dict_index);
- dict_clone_var_assert (dict, var);
- }
- }
-
- /* Construct clip data. */
- map = case_map_by_name (ds->dict->dict, dict);
- writer = autopaging_writer_create (dict_get_proto (dict));
- RANGE_SET_FOR_EACH (node, rows)
- {
- unsigned long int row;
-
- for (row = node->start; row < node->end; row++)
- {
- struct ccase *old = psppire_data_store_get_case (ds, row);
- if (old != NULL)
- casewriter_write (writer, case_map_execute (map, old));
- else
- casewriter_force_error (writer);
- }
- }
- case_map_destroy (map);
-
- /* Clear data that we copied out, if we're doing a "cut". */
- if (cut && !casewriter_error (writer))
- {
- RANGE_SET_FOR_EACH (node, rows)
- {
- unsigned long int row;
-
- for (row = node->start; row < node->end; row++)
- {
- const struct range_set_node *node2;
-
- RANGE_SET_FOR_EACH (node2, cols)
- {
- int dict_index;
-
- for (dict_index = node2->start; dict_index < node2->end;
- dict_index++)
- {
- struct variable *var;
-
- var = dict_get_var (ds->dict->dict, dict_index);
- psppire_data_store_set_string (ds, "", row,
- var, false);
- }
- }
- }
- }
- }
-
- range_set_destroy (rows);
- range_set_destroy (cols);
-
- *readerp = casewriter_make_reader (writer);
-
- return TRUE;
-}
-
-/* Set the clip from the currently selected range in DATA_SHEET. If CUT is
- true, clears the original data from DATA_SHEET, otherwise leaves the
- original data in-place. */
-static void
-psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet,
- gboolean cut)
-{
- struct casereader *reader;
- struct dictionary *dict;
-
- if (psppire_data_sheet_fetch_clip (data_sheet, cut, &reader, &dict))
- {
- casereader_destroy (clip_datasheet);
- dict_destroy (clip_dict);
-
- clip_datasheet = reader;
- clip_dict = dict;
-
- psppire_data_sheet_update_clipboard (data_sheet);
- }
-}
-
-enum {
- SELECT_FMT_NULL,
- SELECT_FMT_TEXT,
- SELECT_FMT_HTML
-};
-
-
-/* Perform data_out for case CC, variable V, appending to STRING */
-static void
-data_out_g_string (GString *string, const struct variable *v,
- const struct ccase *cc)
-{
- const struct fmt_spec *fs = var_get_print_format (v);
- const union value *val = case_data (cc, v);
-
- char *s = data_out (val, var_get_encoding (v), fs);
-
- g_string_append (string, s);
-
- g_free (s);
-}
-
-static GString *
-clip_to_text (struct casereader *datasheet, struct dictionary *dict)
-{
- casenumber r;
- GString *string;
-
- const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
- const casenumber case_cnt = casereader_get_case_cnt (datasheet);
- const size_t var_cnt = dict_get_var_cnt (dict);
-
- string = g_string_sized_new (10 * val_cnt * case_cnt);
-
- for (r = 0 ; r < case_cnt ; ++r )
- {
- int c;
- struct ccase *cc;
-
- cc = casereader_peek (datasheet, r);
- if (cc == NULL)
- {
- g_warning ("Clipboard seems to have inexplicably shrunk");
- break;
- }
-
- for (c = 0 ; c < var_cnt ; ++c)
- {
- const struct variable *v = dict_get_var (dict, c);
- data_out_g_string (string, v, cc);
- if ( c < val_cnt - 1 )
- g_string_append (string, "\t");
- }
-
- if ( r < case_cnt)
- g_string_append (string, "\n");
-
- case_unref (cc);
- }
-
- return string;
-}
-
-
-static GString *
-clip_to_html (struct casereader *datasheet, struct dictionary *dict)
-{
- casenumber r;
- GString *string;
-
- const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (datasheet));
- const casenumber case_cnt = casereader_get_case_cnt (datasheet);
- const size_t var_cnt = dict_get_var_cnt (dict);
-
- /* Guestimate the size needed */
- string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
-
- g_string_append (string,
- "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
-
- g_string_append (string, "<table>\n");
- for (r = 0 ; r < case_cnt ; ++r )
- {
- int c;
- struct ccase *cc = casereader_peek (datasheet, r);
- if (cc == NULL)
- {
- g_warning ("Clipboard seems to have inexplicably shrunk");
- break;
- }
- g_string_append (string, "<tr>\n");
-
- for (c = 0 ; c < var_cnt ; ++c)
- {
- const struct variable *v = dict_get_var (dict, c);
- g_string_append (string, "<td>");
- data_out_g_string (string, v, cc);
- g_string_append (string, "</td>\n");
- }
-
- g_string_append (string, "</tr>\n");
-
- case_unref (cc);
- }
- g_string_append (string, "</table>\n");
-
- return string;
-}
-
-
-
-static void
-psppire_data_sheet_clipboard_set (GtkSelectionData *selection_data,
- guint info,
- struct casereader *reader,
- struct dictionary *dict)
-{
- GString *string = NULL;
-
- switch (info)
- {
- case SELECT_FMT_TEXT:
- string = clip_to_text (reader, dict);
- break;
- case SELECT_FMT_HTML:
- string = clip_to_html (reader, dict);
- break;
- default:
- g_assert_not_reached ();
- }
-
- gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
- 8,
- (const guchar *) string->str, string->len);
-
- g_string_free (string, TRUE);
-}
-
-static void
-psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard,
- GtkSelectionData *selection_data,
- guint info,
- gpointer data)
-{
- psppire_data_sheet_clipboard_set (selection_data, info,
- clip_datasheet, clip_dict);
-}
-
-static void
-psppire_data_sheet_clipboard_clear_cb (GtkClipboard *clipboard,
- gpointer data)
-{
- dict_destroy (clip_dict);
- clip_dict = NULL;
-
- casereader_destroy (clip_datasheet);
- clip_datasheet = NULL;
-}
-
-
-static const GtkTargetEntry targets[] = {
- { "UTF8_STRING", 0, SELECT_FMT_TEXT },
- { "STRING", 0, SELECT_FMT_TEXT },
- { "TEXT", 0, SELECT_FMT_TEXT },
- { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
- { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
- { "text/plain", 0, SELECT_FMT_TEXT },
- { "text/html", 0, SELECT_FMT_HTML }
-};
-
-
-
-static void
-psppire_data_sheet_update_clipboard (PsppireDataSheet *sheet)
-{
- GtkClipboard *clipboard =
- gtk_widget_get_clipboard (GTK_WIDGET (sheet),
- GDK_SELECTION_CLIPBOARD);
-
- if (!gtk_clipboard_set_with_owner (clipboard, targets,
- G_N_ELEMENTS (targets),
- psppire_data_sheet_clipboard_get_cb,
- psppire_data_sheet_clipboard_clear_cb,
- G_OBJECT (sheet)))
- psppire_data_sheet_clipboard_clear_cb (clipboard, sheet);
-}
-
-static void
-psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet)
-{
- struct range_set *rows, *cols;
- GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (data_sheet));
- if (! PSPPIRE_IS_DATA_WINDOW (top))
- return;
-
- PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top);
- gboolean enable =
- psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols);
-
- if (enable)
- {
- range_set_destroy (rows);
- range_set_destroy (cols);
- }
-
- gtk_widget_set_sensitive (dw->mi_copy, enable);
- gtk_widget_set_sensitive (dw->mi_cut, enable);
-}
-
-static void
-psppire_data_sheet_primary_get_cb (GtkClipboard *clipboard,
- GtkSelectionData *selection_data,
- guint info,
- gpointer data)
-{
- PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
- struct casereader *reader;
- struct dictionary *dict;
-
- if (psppire_data_sheet_fetch_clip (data_sheet, FALSE, &reader, &dict))
- {
- psppire_data_sheet_clipboard_set (selection_data, info,
- reader, dict);
- casereader_destroy (reader);
- dict_destroy (dict);
- }
-}
-
-static void
-psppire_data_sheet_update_primary_selection (PsppireDataSheet *data_sheet,
- gboolean should_own)
-{
- GtkClipboard *clipboard;
- GdkDisplay *display;
-
- display = gtk_widget_get_display (GTK_WIDGET (data_sheet));
- clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_PRIMARY);
- g_return_if_fail (clipboard != NULL);
-
- if (data_sheet->owns_primary_selection && !should_own)
- {
- data_sheet->owns_primary_selection = FALSE;
- gtk_clipboard_clear (clipboard);
- }
- else if (should_own)
- data_sheet->owns_primary_selection =
- gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
- psppire_data_sheet_primary_get_cb,
- NULL, G_OBJECT (data_sheet));
-}
-\f
-/* A callback for when the clipboard contents have been received. */
-static void
-psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard,
- GtkSelectionData *sd,
- gpointer data)
-{
- PsppireDataSheet *data_sheet = data;
- PsppireDataStore *store = data_sheet->data_store;
- struct range_set *rows, *cols;
- gint count = 0;
- gint next_row, next_column;
- gint first_column;
- char *c;
-
- if ( gtk_selection_data_get_length (sd) < 0 )
- return;
-
- if ( gtk_selection_data_get_data_type (sd) != gdk_atom_intern ("UTF8_STRING", FALSE))
- return;
-
- c = (char *) gtk_selection_data_get_data (sd);
-
- /* Get the starting selected position in the data sheet. (Possibly we should
- only paste into the selected range if it's larger than one cell?) */
- if (!psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols))
- return;
- next_row = range_set_first (rows)->start;
- first_column = next_column = range_set_first (cols)->start;
- range_set_destroy (rows);
- range_set_destroy (cols);
-
- g_return_if_fail (next_row >= 0);
- g_return_if_fail (next_column >= 0);
-
- while (count < gtk_selection_data_get_length (sd))
- {
- gint row = next_row;
- gint column = next_column;
- struct variable *var;
- char *s = c;
-
- while (*c != '\t' && *c != '\n' && count < gtk_selection_data_get_length (sd))
- {
- c++;
- count++;
- }
- if ( *c == '\t')
- {
- next_row = row ;
- next_column = column + 1;
- }
- else if ( *c == '\n')
- {
- next_row = row + 1;
- next_column = first_column;
- }
- *c++ = '\0';
- count++;
-
- var = psppire_dict_get_variable (store->dict, column);
- if (var != NULL)
- psppire_data_store_set_string (store, s, row, var, FALSE);
- }
-}
-
-static void
-psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard,
- GdkAtom *atoms,
- gint n_atoms,
- gpointer data)
-{
- GtkWidget *mi = GTK_WIDGET (data);
- gboolean compatible_target = FALSE;
- gint i;
- for (i = 0; i < G_N_ELEMENTS (targets); i++)
- {
- GdkAtom target = gdk_atom_intern (targets[i].target, TRUE);
- gint j;
-
- for (j = 0; j < n_atoms; j++)
- if (target == atoms[j])
- {
- compatible_target = TRUE;
- break;
- }
- }
-
- gtk_widget_set_sensitive (mi, compatible_target);
-}
-
-static void
-on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
-{
- PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data);
-
- GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (data_sheet));
- if (! PSPPIRE_IS_DATA_WINDOW (top))
- return;