X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fgui%2Fpsppire-data-sheet.c;h=dbc92159777723e87b9a97c0cf67c97fea241b8e;hb=43f0e5bf6c53ee0ba31b259aab230861baa4eb7e;hp=4960a838364c008167a8158bb3306c32c4702e7a;hpb=a67af165ac29e2daadf950a84b536b6b7b408a31;p=pspp diff --git a/src/ui/gui/psppire-data-sheet.c b/src/ui/gui/psppire-data-sheet.c index 4960a83836..dbc9215977 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 @@ -51,6 +51,8 @@ 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_update_primary_selection (PsppireDataSheet *, + gboolean should_own); static void psppire_data_sheet_set_clip (PsppireDataSheet *, gboolean cut); static void on_selection_changed (PsppSheetSelection *, gpointer); @@ -122,6 +124,7 @@ on_query_tooltip (GtkWidget *widget, gint wx, gint wy, int width; g_return_val_if_fail (data_store != NULL, FALSE); + g_return_val_if_fail (data_store->datasheet != NULL, FALSE); if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column)) return FALSE; @@ -174,9 +177,9 @@ render_row_number_cell (PsppSheetViewColumn *tree_column, { PsppireDataStore *store = store_; GValue gvalue = { 0, }; - gint row; + gint row = GPOINTER_TO_INT (iter->user_data); - row = GPOINTER_TO_INT (iter->user_data); + g_return_if_fail (store->datasheet); g_value_init (&gvalue, G_TYPE_INT); g_value_set_int (&gvalue, row + 1); @@ -288,8 +291,9 @@ get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer, { gint width; g_object_set (G_OBJECT (renderer), "text", string, (void *) NULL); - gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview), - NULL, NULL, NULL, &width, NULL); + gtk_cell_renderer_get_preferred_width (renderer, GTK_WIDGET (treeview), + NULL, &width); + return width; } @@ -335,13 +339,15 @@ on_data_column_editing_started (GtkCellRenderer *cell, if (var_has_value_labels (var) && GTK_IS_COMBO_BOX (editable)) { const struct val_labs *labels = var_get_value_labels (var); - const struct val_lab *vl; + const struct val_lab **vls = val_labs_sorted (labels); + size_t n_vls = val_labs_count (labels); GtkListStore *list_store; + int i; list_store = gtk_list_store_new (1, G_TYPE_STRING); - for (vl = val_labs_first (labels); vl != NULL; - vl = val_labs_next (labels, vl)) + for (i = 0; i < n_vls; ++i) { + const struct val_lab *vl = vls[i]; GtkTreeIter iter; gtk_list_store_append (list_store, &iter); @@ -349,6 +355,7 @@ on_data_column_editing_started (GtkCellRenderer *cell, 0, val_lab_get_label (vl), -1); } + free (vls); gtk_combo_box_set_model (GTK_COMBO_BOX (editable), GTK_TREE_MODEL (list_store)); @@ -414,7 +421,7 @@ on_data_column_edited (GtkCellRendererText *cell, { gtk_widget_queue_resize (GTK_WIDGET (data_sheet)); data_sheet->scroll_to_bottom_signal = - g_signal_connect (data_sheet, "size-request", + g_signal_connect (data_sheet, "size-allocate", G_CALLBACK (scroll_to_bottom), NULL); } else @@ -669,7 +676,7 @@ add_data_column_cell_renderer (PsppireDataSheet *data_sheet, var = g_object_get_data (G_OBJECT (column), "variable"); g_return_if_fail (var != NULL); - if (var_has_value_labels (var)) + if (data_sheet->show_value_labels && var_has_value_labels (var)) { cell = gtk_cell_renderer_combo_new (); g_object_set (G_OBJECT (cell), @@ -938,10 +945,11 @@ psppire_data_sheet_set_value_labels (PsppireDataSheet *ds, { ds->show_value_labels = show_value_labels; g_object_notify (G_OBJECT (ds), "value-labels"); - gtk_widget_queue_draw (GTK_WIDGET (ds)); - /* Make the cell being edited refresh too. */ - pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (ds), TRUE); + /* Pretend the model changed, to force the columns to be rebuilt. + Otherwise cell renderers won't get changed from combo boxes to text + entries or vice versa. */ + g_object_notify (G_OBJECT (ds), "model"); } } @@ -1047,7 +1055,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); @@ -1201,6 +1209,13 @@ psppire_data_sheet_dispose (GObject *object) { PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (object); + if (data_sheet->clip != NULL && data_sheet->on_owner_change_signal != 0) + { + g_signal_handler_disconnect (data_sheet->clip, + data_sheet->on_owner_change_signal); + data_sheet->on_owner_change_signal = 0; + } + if (data_sheet->dispose_has_run) return; @@ -1219,14 +1234,19 @@ psppire_data_sheet_dispose (GObject *object) static void psppire_data_sheet_map (GtkWidget *widget) { - GtkClipboard *clip; + PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); GTK_WIDGET_CLASS (psppire_data_sheet_parent_class)->map (widget); - clip = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD); - g_signal_connect (clip, "owner-change", G_CALLBACK (on_owner_change), - widget); - on_owner_change (clip, NULL, 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 @@ -1389,6 +1409,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; @@ -1421,6 +1442,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) @@ -1431,6 +1453,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) @@ -1454,6 +1477,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 @@ -1678,8 +1704,11 @@ 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; @@ -1883,6 +1912,7 @@ on_variable_display_width_changed (PsppireDict *dict, int dict_index, 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); @@ -1895,11 +1925,16 @@ on_variable_changed (PsppireDict *dict, int dict_index, 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); @@ -2019,9 +2054,6 @@ psppire_data_sheet_set_data_store (PsppireDataSheet *data_sheet, g_signal_connect (data_store->dict, "variable-changed", G_CALLBACK (on_variable_changed), data_sheet); - g_signal_connect (data_store->dict, "variable-display-width-changed", - G_CALLBACK (on_variable_display_width_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", @@ -2039,38 +2071,27 @@ static struct dictionary *clip_dict = NULL; static void psppire_data_sheet_update_clipboard (PsppireDataSheet *); -/* 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) +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; @@ -2078,13 +2099,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; @@ -2132,9 +2153,31 @@ psppire_data_sheet_set_clip (PsppireDataSheet *data_sheet, range_set_destroy (rows); range_set_destroy (cols); - clip_datasheet = casewriter_make_reader (writer); + *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); + psppire_data_sheet_update_clipboard (data_sheet); + } } enum { @@ -2160,14 +2203,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); @@ -2176,7 +2219,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"); @@ -2185,7 +2228,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"); @@ -2202,14 +2245,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); @@ -2221,7 +2264,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"); @@ -2231,7 +2274,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"); @@ -2249,32 +2292,42 @@ 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 (); } - gtk_selection_data_set (selection_data, selection_data->target, + 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) @@ -2334,6 +2387,48 @@ psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet) action = get_action_assert (data_sheet->builder, "edit_cut"); 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 @@ -2349,13 +2444,13 @@ psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard, gint first_column; char *c; - if ( sd->length < 0 ) + if ( gtk_selection_data_get_length (sd) < 0 ) return; - if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE)) + if ( gtk_selection_data_get_data_type (sd) != gdk_atom_intern ("UTF8_STRING", FALSE)) return; - c = (char *) sd->data; + 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?) */ @@ -2369,14 +2464,14 @@ psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard, g_return_if_fail (next_row >= 0); g_return_if_fail (next_column >= 0); - while (count < sd->length) + 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 < sd->length) + while (*c != '\t' && *c != '\n' && count < gtk_selection_data_get_length (sd)) { c++; count++; @@ -2401,23 +2496,40 @@ psppire_data_sheet_clip_received_cb (GtkClipboard *clipboard, } static void -on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data) +psppire_data_sheet_targets_received_cb (GtkClipboard *clipboard, + GdkAtom *atoms, + gint n_atoms, + gpointer data) { - PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (data); - gboolean compatible_target = FALSE; - GtkAction *action; + GtkAction *action = GTK_ACTION (data); + gboolean compatible_target; gint i; + compatible_target = FALSE; for (i = 0; i < G_N_ELEMENTS (targets); i++) { - GdkAtom atom = gdk_atom_intern (targets[i].target, TRUE); - if (gtk_clipboard_wait_is_target_available (clip, atom)) - { - compatible_target = TRUE; - break; - } + 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; + } } - action = get_action_assert (data_sheet->builder, "edit_paste"); 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); }