X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fgui%2Fpsppire-data-sheet.c;h=01afb5ed8772e50a9bd136241de385429a0d646e;hb=8ceafd2d6c622fdf627013cbe634127f8e172328;hp=b25b189e2c2dbd86cc75f4e237721be391f5474b;hpb=0fe886a656b0ee57b4d542b7c16b3c56288b6fc6;p=pspp diff --git a/src/ui/gui/psppire-data-sheet.c b/src/ui/gui/psppire-data-sheet.c index b25b189e2c..01afb5ed87 100644 --- a/src/ui/gui/psppire-data-sheet.c +++ b/src/ui/gui/psppire-data-sheet.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2011, 2012 Free Software Foundation, Inc. + Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,13 +47,18 @@ #define _(msgid) gettext (msgid) #define N_(msgid) msgid -static void psppire_data_sheet_destroy (GtkObject *); +static void psppire_data_sheet_dispose (GObject *); static void psppire_data_sheet_unset_data_store (PsppireDataSheet *); static void psppire_data_sheet_update_clip_actions (PsppireDataSheet *); -static void psppire_data_sheet_set_clip (PsppireDataSheet *); +static void psppire_data_sheet_update_primary_selection (PsppireDataSheet *, + gboolean should_own); +static void psppire_data_sheet_set_clip (PsppireDataSheet *, gboolean cut); static void on_selection_changed (PsppSheetSelection *, gpointer); +static void on_owner_change (GtkClipboard *, GdkEventOwnerChange *, gpointer); +static void psppire_data_sheet_clip_received_cb (GtkClipboard *, + GtkSelectionData *, gpointer); G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, PSPP_TYPE_SHEET_VIEW); @@ -223,7 +228,10 @@ make_row_number_column (PsppireDataSheet *data_sheet, column = pspp_sheet_view_column_new_with_attributes (_("Case"), renderer, NULL); - g_object_set (column, "selectable", FALSE, "row-head", TRUE, NULL); + pspp_sheet_view_column_set_selectable (column, TRUE); + pspp_sheet_view_column_set_row_head (column, TRUE); + pspp_sheet_view_column_set_tabbable (column, FALSE); + pspp_sheet_view_column_set_clickable (column, TRUE); pspp_sheet_view_column_set_cell_data_func ( column, renderer, render_row_number_cell, ds, NULL); pspp_sheet_view_column_set_fixed_width (column, 50); @@ -752,6 +760,7 @@ make_new_variable_column (PsppireDataSheet *data_sheet, width = display_width_to_pixel_width (data_sheet, 8, base_width, incr_width); pspp_sheet_view_column_set_min_width (column, 10); pspp_sheet_view_column_set_fixed_width (column, width); + pspp_sheet_view_column_set_tabbable (column, FALSE); g_object_set_data (G_OBJECT (cell), "data-sheet", data_sheet); g_signal_connect (column, "button-press-event", @@ -1040,7 +1049,7 @@ psppire_data_sheet_find_column_for_variable (PsppireDataSheet *data_sheet, } void -psppire_data_sheet_show_variable (PsppireDataSheet *data_sheet, +psppire_data_sheet_goto_variable (PsppireDataSheet *data_sheet, gint dict_index) { PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); @@ -1049,8 +1058,17 @@ psppire_data_sheet_show_variable (PsppireDataSheet *data_sheet, column = psppire_data_sheet_find_column_for_variable (data_sheet, dict_index); if (column != NULL) - pspp_sheet_view_scroll_to_cell (sheet_view, NULL, column, - FALSE, 0.0, 0.0); + { + GtkTreePath *path; + + gint row = psppire_data_sheet_get_current_case (data_sheet); + path = gtk_tree_path_new_from_indices (row >= 0 ? row : 0, -1); + + pspp_sheet_view_scroll_to_cell (sheet_view, path, column, + FALSE, 0.0, 0.0); + pspp_sheet_view_set_cursor (sheet_view, path, column, FALSE); + gtk_tree_path_free (path); + } } struct variable * @@ -1168,38 +1186,76 @@ psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet) GtkUIManager * psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet) { - return GTK_UI_MANAGER (get_object_assert (data_sheet->builder, - "data_sheet_uim", - GTK_TYPE_UI_MANAGER)); + if (data_sheet->uim == NULL) + { + data_sheet->uim = + GTK_UI_MANAGER (get_object_assert (data_sheet->builder, + "data_sheet_uim", + GTK_TYPE_UI_MANAGER)); + g_object_ref (data_sheet->uim); + } + + return data_sheet->uim; } static void -psppire_data_sheet_destroy (GtkObject *object) +psppire_data_sheet_dispose (GObject *object) { PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object); - psppire_data_sheet_unset_data_store (data_sheet); - if (data_sheet->builder) + if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0) { - g_object_unref (data_sheet->builder); - data_sheet->builder = NULL; + g_signal_handler_disconnect (data_sheet->clip, + data_sheet->on_owner_change_signal); + data_sheet->on_owner_change_signal = 0; } - GTK_OBJECT_CLASS (psppire_data_sheet_parent_class)->destroy (object); + if (data_sheet->dispose_has_run) + return; + + data_sheet->dispose_has_run = TRUE; + + psppire_data_sheet_unset_data_store (data_sheet); + + g_object_unref (data_sheet->builder); + + if (data_sheet->uim) + g_object_unref (data_sheet->uim); + + G_OBJECT_CLASS (psppire_data_sheet_parent_class)->dispose (object); +} + +static void +psppire_data_sheet_map (GtkWidget *widget) +{ + PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); + + GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget); + + data_sheet->clip = gtk_widget_get_clipboard (widget, + GDK_SELECTION_CLIPBOARD); + if (data_sheet->on_owner_change_signal) + g_signal_handler_disconnect (data_sheet->clip, + data_sheet->on_owner_change_signal); + data_sheet->on_owner_change_signal + = g_signal_connect (data_sheet->clip, "owner-change", + G_CALLBACK (on_owner_change), widget); + on_owner_change (data_sheet->clip, NULL, widget); } static void psppire_data_sheet_class_init (PsppireDataSheetClass *class) { GObjectClass *gobject_class; - GtkObjectClass *gtk_object_class; + GtkWidgetClass *widget_class; gobject_class = G_OBJECT_CLASS (class); gobject_class->set_property = psppire_data_sheet_set_property; gobject_class->get_property = psppire_data_sheet_get_property; + gobject_class->dispose = psppire_data_sheet_dispose; - gtk_object_class = GTK_OBJECT_CLASS (class); - gtk_object_class->destroy = psppire_data_sheet_destroy; + widget_class = GTK_WIDGET_CLASS (class); + widget_class->map = psppire_data_sheet_map; g_signal_new ("var-double-clicked", G_OBJECT_CLASS_TYPE (gobject_class), @@ -1347,6 +1403,7 @@ on_selection_changed (PsppSheetSelection *selection, PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection); PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view); gint n_selected_rows; + gboolean any_variables_selected; gboolean may_delete_cases, may_delete_vars, may_insert_vars; GList *list, *iter; GtkTreePath *path; @@ -1379,6 +1436,7 @@ on_selection_changed (PsppSheetSelection *selection, action = get_action_assert (data_sheet->builder, "edit_clear-cases"); gtk_action_set_sensitive (action, may_delete_cases); + any_variables_selected = FALSE; may_delete_vars = may_insert_vars = FALSE; list = pspp_sheet_selection_get_selected_columns (selection); for (iter = list; iter != NULL; iter = iter->next) @@ -1389,6 +1447,7 @@ on_selection_changed (PsppSheetSelection *selection, if (var != NULL) { may_delete_vars = may_insert_vars = TRUE; + any_variables_selected = TRUE; break; } if (g_object_get_data (G_OBJECT (column), "new-var-column") != NULL) @@ -1412,6 +1471,9 @@ on_selection_changed (PsppSheetSelection *selection, gtk_action_set_sensitive (action, may_delete_vars); psppire_data_sheet_update_clip_actions (data_sheet); + psppire_data_sheet_update_primary_selection (data_sheet, + (n_selected_rows > 0 + && any_variables_selected)); } static gboolean @@ -1603,7 +1665,26 @@ on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet) void on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet) { - psppire_data_sheet_set_clip (data_sheet); + psppire_data_sheet_set_clip (data_sheet, FALSE); +} + +void +on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet) +{ + psppire_data_sheet_set_clip (data_sheet, TRUE); +} + +void +on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet) +{ + GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet)); + GtkClipboard *clipboard = + gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD); + + gtk_clipboard_request_contents (clipboard, + gdk_atom_intern ("UTF8_STRING", TRUE), + psppire_data_sheet_clip_received_cb, + data_sheet); } static void @@ -1617,11 +1698,17 @@ psppire_data_sheet_init (PsppireDataSheet *obj) obj->may_create_vars = TRUE; obj->may_delete_vars = TRUE; + obj->owns_primary_selection = FALSE; + obj->scroll_to_bottom_signal = 0; obj->scroll_to_right_signal = 0; + obj->on_owner_change_signal = 0; obj->new_variable_column = NULL; obj->container = NULL; + obj->uim = NULL; + obj->dispose_has_run = FALSE; + pspp_sheet_view_set_special_cells (sheet_view, PSPP_SHEET_VIEW_SPECIAL_CELLS_YES); g_signal_connect (obj, "notify::model", @@ -1662,6 +1749,12 @@ psppire_data_sheet_init (PsppireDataSheet *obj) action = get_action_assert (obj->builder, "edit_copy"); g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), obj); + action = get_action_assert (obj->builder, "edit_cut"); + g_signal_connect (action, "activate", G_CALLBACK (on_edit_cut), obj); + + action = get_action_assert (obj->builder, "edit_paste"); + g_signal_connect (action, "activate", G_CALLBACK (on_edit_paste), obj); + action = get_action_assert (obj->builder, "edit_clear-variables"); g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables), obj); @@ -1969,36 +2062,27 @@ static struct dictionary *clip_dict = NULL; static void psppire_data_sheet_update_clipboard (PsppireDataSheet *); -/* Set the clip according to the currently - selected range in the data sheet */ -static void -psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet) +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)) - return; - - - /* Destroy any existing clip */ - if ( clip_datasheet ) { - casereader_destroy (clip_datasheet); - clip_datasheet = NULL; - } - - if ( clip_dict ) - { - dict_destroy (clip_dict); - clip_dict = NULL; + *readerp = NULL; + *dictp = NULL; + return FALSE; } /* Construct clip dictionary. */ - clip_dict = dict_create (dict_get_encoding (ds->dict->dict)); + *dictp = dict = dict_create (dict_get_encoding (ds->dict->dict)); RANGE_SET_FOR_EACH (node, cols) { int dict_index; @@ -2006,13 +2090,13 @@ psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet) 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 (clip_dict, var); + dict_clone_var_assert (dict, var); } } /* Construct clip data. */ - map = case_map_by_name (ds->dict->dict, clip_dict); - writer = autopaging_writer_create (dict_get_proto (clip_dict)); + 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; @@ -2028,12 +2112,63 @@ psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet) } 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); - clip_datasheet = casewriter_make_reader (writer); + *readerp = casewriter_make_reader (writer); - psppire_data_sheet_update_clipboard (data_sheet); + 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 { @@ -2059,14 +2194,14 @@ data_out_g_string (GString *string, const struct variable *v, } static GString * -clip_to_text (void) +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 (clip_datasheet)); - const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet); - const size_t var_cnt = dict_get_var_cnt (clip_dict); + 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); @@ -2075,7 +2210,7 @@ clip_to_text (void) int c; struct ccase *cc; - cc = casereader_peek (clip_datasheet, r); + cc = casereader_peek (datasheet, r); if (cc == NULL) { g_warning ("Clipboard seems to have inexplicably shrunk"); @@ -2084,7 +2219,7 @@ clip_to_text (void) for (c = 0 ; c < var_cnt ; ++c) { - const struct variable *v = dict_get_var (clip_dict, 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"); @@ -2101,14 +2236,14 @@ clip_to_text (void) static GString * -clip_to_html (void) +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 (clip_datasheet)); - const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet); - const size_t var_cnt = dict_get_var_cnt (clip_dict); + 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); @@ -2120,7 +2255,7 @@ clip_to_html (void) for (r = 0 ; r < case_cnt ; ++r ) { int c; - struct ccase *cc = casereader_peek (clip_datasheet, r); + struct ccase *cc = casereader_peek (datasheet, r); if (cc == NULL) { g_warning ("Clipboard seems to have inexplicably shrunk"); @@ -2130,7 +2265,7 @@ clip_to_html (void) for (c = 0 ; c < var_cnt ; ++c) { - const struct variable *v = dict_get_var (clip_dict, c); + const struct variable *v = dict_get_var (dict, c); g_string_append (string, ""); data_out_g_string (string, v, cc); g_string_append (string, "\n"); @@ -2148,20 +2283,20 @@ clip_to_html (void) static void -psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard, - GtkSelectionData *selection_data, - guint info, - gpointer data) +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 (); + string = clip_to_text (reader, dict); break; case SELECT_FMT_HTML: - string = clip_to_html (); + string = clip_to_html (reader, dict); break; default: g_assert_not_reached (); @@ -2174,6 +2309,16 @@ psppire_data_sheet_clipboard_get_cb (GtkClipboard *clipboard, 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) @@ -2234,3 +2379,148 @@ psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet) gtk_action_set_sensitive (action, 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)); +} + +/* 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 ( sd->length < 0 ) + return; + + if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE)) + return; + + c = (char *) sd->data; + + /* 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 < sd->length) + { + gint row = next_row; + gint column = next_column; + struct variable *var; + char *s = c; + + while (*c != '\t' && *c != '\n' && count < sd->length) + { + 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) +{ + GtkAction *action = GTK_ACTION (data); + gboolean compatible_target; + gint i; + + compatible_target = FALSE; + 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_action_set_sensitive (action, compatible_target); + g_object_unref (action); +} + +static void +on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data) +{ + PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data); + GtkAction *action = get_action_assert (data_sheet->builder, "edit_paste"); + + g_object_ref (action); + gtk_clipboard_request_targets (clip, psppire_data_sheet_targets_received_cb, + action); +}