X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fgui%2Fpsppire-data-sheet.c;h=962fa1ef2b07f44cee6be9b65c876e63ca7fd1a9;hb=d5a566a2900f69ab51c7dae78cd7810a4204589a;hp=e1ecbbb924a005bd0646c62a3558765965814fa1;hpb=21bed3a6d98451db78a9f327e951080e42b44387;p=pspp diff --git a/src/ui/gui/psppire-data-sheet.c b/src/ui/gui/psppire-data-sheet.c index e1ecbbb924..962fa1ef2b 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 @@ -584,16 +591,71 @@ on_data_column_resized (GObject *gobject, var_set_display_width (var, display_width); } +enum sort_order + { + SORT_ASCEND, + SORT_DESCEND + }; + +static void +do_sort (PsppireDataSheet *data_sheet, enum sort_order order) +{ + PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); + PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); + PsppireDataWindow *pdw; + GList *list, *iter; + GString *syntax; + int n_vars; + + pdw = psppire_data_window_for_data_store (data_sheet->data_store); + g_return_if_fail (pdw != NULL); + + list = pspp_sheet_selection_get_selected_columns (selection); + + syntax = g_string_new ("SORT CASES BY"); + n_vars = 0; + for (iter = list; iter; iter = iter->next) + { + PsppSheetViewColumn *column = iter->data; + struct variable *var; + + var = g_object_get_data (G_OBJECT (column), "variable"); + if (var != NULL) + { + g_string_append_printf (syntax, " %s", var_get_name (var)); + n_vars++; + } + } + if (n_vars > 0) + { + if (order == SORT_DESCEND) + g_string_append (syntax, " (DOWN)"); + g_string_append_c (syntax, '.'); + execute_const_syntax_string (pdw, syntax->str); + } + g_string_free (syntax, TRUE); +} + +static void +on_sort_up (PsppireDataSheet *data_sheet) +{ + do_sort (data_sheet, SORT_ASCEND); +} + +static void +on_sort_down (PsppireDataSheet *data_sheet) +{ + do_sort (data_sheet, SORT_DESCEND); +} + static void do_data_column_popup_menu (PsppSheetViewColumn *column, guint button, guint32 time) { GtkWidget *sheet_view = pspp_sheet_view_column_get_tree_view (column); PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (sheet_view); - GtkWidget *menu; - menu = get_widget_assert (data_sheet->builder, "datasheet-variable-popup"); - gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time); + gtk_menu_popup (GTK_MENU (data_sheet->column_popup_menu), NULL, NULL, NULL, NULL, button, time); } static void @@ -669,7 +731,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), @@ -831,8 +893,7 @@ enum PROP_CASE_NUMBERS, PROP_CURRENT_CASE, PROP_MAY_CREATE_VARS, - PROP_MAY_DELETE_VARS, - PROP_UI_MANAGER + PROP_MAY_DELETE_VARS }; static void @@ -872,7 +933,6 @@ psppire_data_sheet_set_property (GObject *object, g_value_get_boolean (value)); break; - case PROP_UI_MANAGER: default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -913,10 +973,6 @@ psppire_data_sheet_get_property (GObject *object, g_value_set_boolean (value, obj->may_delete_vars); break; - case PROP_UI_MANAGER: - g_value_set_object (value, psppire_data_sheet_get_ui_manager (obj)); - break; - default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -938,10 +994,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"); } } @@ -1181,26 +1238,19 @@ psppire_data_sheet_get_current_case (const PsppireDataSheet *data_sheet) return row; } -GtkUIManager * -psppire_data_sheet_get_ui_manager (PsppireDataSheet *data_sheet) -{ - 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_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; @@ -1208,25 +1258,25 @@ psppire_data_sheet_dispose (GObject *object) 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) { - 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 @@ -1294,6 +1344,7 @@ psppire_data_sheet_class_init (PsppireDataSheetClass *class) TRUE, G_PARAM_READWRITE)); + g_object_class_install_property ( gobject_class, PROP_MAY_DELETE_VARS, @@ -1302,31 +1353,21 @@ psppire_data_sheet_class_init (PsppireDataSheetClass *class) "Whether the user may delete variables", TRUE, G_PARAM_READWRITE)); - - g_object_class_install_property ( - gobject_class, - PROP_UI_MANAGER, - g_param_spec_object ("ui-manager", - "UI Manager", - "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.", - GTK_TYPE_UI_MANAGER, - G_PARAM_READABLE)); } static void -do_popup_menu (GtkWidget *widget, guint button, guint32 time) +do_row_popup_menu (GtkWidget *widget, guint button, guint32 time) { PsppireDataSheet *data_sheet = PSPPIRE_DATA_SHEET (widget); - GtkWidget *menu; - menu = get_widget_assert (data_sheet->builder, "datasheet-cases-popup"); - gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time); + + gtk_menu_popup (GTK_MENU (data_sheet->row_popup_menu), NULL, NULL, NULL, NULL, button, time); } static void on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED) { - do_popup_menu (widget, 0, gtk_get_current_event_time ()); + do_row_popup_menu (widget, 0, gtk_get_current_event_time ()); } static gboolean @@ -1354,7 +1395,7 @@ on_button_pressed (GtkWidget *widget, GdkEventButton *event, } } - do_popup_menu (widget, event->button, event->time); + do_row_popup_menu (widget, event->button, event->time); return TRUE; } @@ -1362,8 +1403,8 @@ on_button_pressed (GtkWidget *widget, GdkEventButton *event, return FALSE; } -static void -on_edit_clear_cases (GtkAction *action, PsppireDataSheet *data_sheet) +void +psppire_data_sheet_edit_clear_cases (PsppireDataSheet *data_sheet) { PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); @@ -1388,16 +1429,20 @@ 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; - GtkAction *action; - n_selected_rows = pspp_sheet_selection_count_selected_rows (selection); + GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (data_sheet)); + if (! PSPPIRE_IS_DATA_WINDOW (top)) + return; + + PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top); + + gint n_selected_rows = pspp_sheet_selection_count_selected_rows (selection); - action = get_action_assert (data_sheet->builder, "edit_insert-case"); - gtk_action_set_sensitive (action, n_selected_rows > 0); + gtk_widget_set_sensitive (dw->mi_insert_case, n_selected_rows > 0); switch (n_selected_rows) { @@ -1418,11 +1463,13 @@ on_selection_changed (PsppSheetSelection *selection, may_delete_cases = TRUE; break; } - action = get_action_assert (data_sheet->builder, "edit_clear-cases"); - gtk_action_set_sensitive (action, may_delete_cases); + gtk_widget_set_sensitive (dw->mi_clear_cases, 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) { PsppSheetViewColumn *column = iter->data; @@ -1431,6 +1478,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) @@ -1441,19 +1489,15 @@ on_selection_changed (PsppSheetSelection *selection, may_insert_vars = may_insert_vars && data_sheet->may_create_vars; may_delete_vars = may_delete_vars && data_sheet->may_delete_vars; - action = get_action_assert (data_sheet->builder, "edit_insert-variable"); - gtk_action_set_sensitive (action, may_insert_vars); - - action = get_action_assert (data_sheet->builder, "edit_clear-variables"); - gtk_action_set_sensitive (action, may_delete_vars); - - action = get_action_assert (data_sheet->builder, "sort-up"); - gtk_action_set_sensitive (action, may_delete_vars); - - action = get_action_assert (data_sheet->builder, "sort-down"); - gtk_action_set_sensitive (action, may_delete_vars); + gtk_widget_set_sensitive (dw->mi_insert_var, may_insert_vars); + gtk_widget_set_sensitive (dw->mi_clear_variables, may_delete_vars); + gtk_widget_set_sensitive (data_sheet->pu_sort_up, may_delete_vars); + gtk_widget_set_sensitive (data_sheet->pu_sort_down, 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 @@ -1503,25 +1547,23 @@ psppire_data_sheet_get_selected_range (PsppireDataSheet *data_sheet, return TRUE; } -static void -on_edit_insert_case (GtkAction *action, PsppireDataSheet *data_sheet) +/* Insert a case at the selected row */ +void +psppire_data_sheet_insert_case (PsppireDataSheet *data_sheet) { PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); PsppireDataStore *data_store = data_sheet->data_store; - struct range_set *selected; - unsigned long row; - - selected = pspp_sheet_selection_get_range_set (selection); - row = range_set_scan (selected, 0); + struct range_set *selected = pspp_sheet_selection_get_range_set (selection); + unsigned long row = range_set_scan (selected, 0); range_set_destroy (selected); if (row <= psppire_data_store_get_case_count (data_store)) psppire_data_store_insert_new_case (data_store, row); } -static void -on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet) +void +psppire_data_sheet_insert_variable (PsppireDataSheet *data_sheet) { PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); @@ -1544,118 +1586,42 @@ on_edit_insert_variable (GtkAction *action, PsppireDataSheet *data_sheet) psppire_dict_insert_variable (dict, index, name); } -static void -on_edit_clear_variables (GtkAction *action, PsppireDataSheet *data_sheet) +void +psppire_data_sheet_edit_clear_variables (PsppireDataSheet *data_sheet) { PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); PsppireDict *dict = data_sheet->data_store->dict; - GList *list, *iter; + GList *iter; + GList *list = pspp_sheet_selection_get_selected_columns (selection); - list = pspp_sheet_selection_get_selected_columns (selection); if (list == NULL) return; list = g_list_reverse (list); for (iter = list; iter; iter = iter->next) { PsppSheetViewColumn *column = iter->data; - struct variable *var; - - var = g_object_get_data (G_OBJECT (column), "variable"); + struct variable *var = g_object_get_data (G_OBJECT (column), "variable"); if (var != NULL) psppire_dict_delete_variables (dict, var_get_dict_index (var), 1); } g_list_free (list); } -enum sort_order - { - SORT_ASCEND, - SORT_DESCEND - }; - -static void -do_sort (PsppireDataSheet *data_sheet, enum sort_order order) -{ - PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet); - PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view); - PsppireDataWindow *pdw; - GList *list, *iter; - GString *syntax; - int n_vars; - - pdw = psppire_data_window_for_data_store (data_sheet->data_store); - g_return_if_fail (pdw != NULL); - - list = pspp_sheet_selection_get_selected_columns (selection); - - syntax = g_string_new ("SORT CASES BY"); - n_vars = 0; - for (iter = list; iter; iter = iter->next) - { - PsppSheetViewColumn *column = iter->data; - struct variable *var; - - var = g_object_get_data (G_OBJECT (column), "variable"); - if (var != NULL) - { - g_string_append_printf (syntax, " %s", var_get_name (var)); - n_vars++; - } - } - if (n_vars > 0) - { - if (order == SORT_DESCEND) - g_string_append (syntax, " (DOWN)"); - g_string_append_c (syntax, '.'); - execute_const_syntax_string (pdw, syntax->str); - } - g_string_free (syntax, TRUE); -} - -void -on_sort_up (GtkAction *action, PsppireDataSheet *data_sheet) -{ - do_sort (data_sheet, SORT_ASCEND); -} - -void -on_sort_down (GtkAction *action, PsppireDataSheet *data_sheet) -{ - do_sort (data_sheet, SORT_DESCEND); -} - -void -on_edit_goto_case (GtkAction *action, PsppireDataSheet *data_sheet) -{ - goto_case_dialog (data_sheet); -} - -void -on_edit_find (GtkAction *action, PsppireDataSheet *data_sheet) -{ - PsppireDataWindow *pdw; - - pdw = psppire_data_window_for_data_store (data_sheet->data_store); - g_return_if_fail (pdw != NULL); - - find_dialog (pdw); -} - void -on_edit_copy (GtkAction *action, PsppireDataSheet *data_sheet) +psppire_data_sheet_edit_copy (PsppireDataSheet *data_sheet) { psppire_data_sheet_set_clip (data_sheet, FALSE); } void -on_edit_cut (GtkAction *action, PsppireDataSheet *data_sheet) +psppire_data_sheet_edit_cut (PsppireDataSheet *data_sheet) { psppire_data_sheet_set_clip (data_sheet, TRUE); } void -on_edit_paste (GtkAction *action, PsppireDataSheet *data_sheet) +psppire_data_sheet_edit_paste (PsppireDataSheet *data_sheet) { GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (data_sheet)); GtkClipboard *clipboard = @@ -1671,23 +1637,67 @@ static void psppire_data_sheet_init (PsppireDataSheet *obj) { PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj); - GtkAction *action; obj->show_value_labels = FALSE; obj->show_case_numbers = TRUE; 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); + { + obj->row_popup_menu = gtk_menu_new (); + int i = 0; + + GtkWidget *insert_case = gtk_menu_item_new_with_mnemonic (_("_Insert Case")); + GtkWidget *clear_cases = gtk_menu_item_new_with_mnemonic (_("Cl_ear Cases")); + + gtk_menu_attach (GTK_MENU (obj->row_popup_menu), insert_case, 0, 1, i, i + 1); ++i; + gtk_menu_attach (GTK_MENU (obj->row_popup_menu), clear_cases, 0, 1, i, i + 1); ++i; + + g_signal_connect_swapped (clear_cases, "activate", G_CALLBACK (psppire_data_sheet_edit_clear_cases), obj); + g_signal_connect_swapped (insert_case, "activate", G_CALLBACK (psppire_data_sheet_insert_case), obj); + + gtk_widget_show_all (obj->row_popup_menu); + } + + { + obj->column_popup_menu = gtk_menu_new (); + int i = 0; + + GtkWidget *insert_variable = gtk_menu_item_new_with_mnemonic (_("_Insert Variable")); + GtkWidget *clear_variables = gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables")); + obj->pu_sort_up = gtk_menu_item_new_with_mnemonic (_("Sort _Ascending")); + obj->pu_sort_down = gtk_menu_item_new_with_mnemonic (_("Sort _Descending")); + + g_signal_connect_swapped (clear_variables, "activate", G_CALLBACK (psppire_data_sheet_edit_clear_variables), obj); + g_signal_connect_swapped (insert_variable, "activate", G_CALLBACK (psppire_data_sheet_insert_variable), obj); + + g_signal_connect_swapped (obj->pu_sort_up, "activate", G_CALLBACK (on_sort_up), obj); + g_signal_connect_swapped (obj->pu_sort_down, "activate", G_CALLBACK (on_sort_down), obj); + + gtk_menu_attach (GTK_MENU (obj->column_popup_menu), insert_variable, 0, 1, i, i + 1); ++i; + gtk_menu_attach (GTK_MENU (obj->column_popup_menu), clear_variables, 0, 1, i, i + 1); ++i; + + gtk_menu_attach (GTK_MENU (obj->column_popup_menu), gtk_separator_menu_item_new (), 0, 1, i, i + 1); ++i; + + gtk_menu_attach (GTK_MENU (obj->column_popup_menu), obj->pu_sort_up, 0, 1, i, i + 1); ++i; + gtk_menu_attach (GTK_MENU (obj->column_popup_menu), obj->pu_sort_down, 0, 1, i, i + 1); ++i; + + gtk_widget_show_all (obj->column_popup_menu); + } + + g_signal_connect (obj, "notify::model", G_CALLBACK (psppire_data_sheet_model_changed), NULL); @@ -1700,51 +1710,11 @@ psppire_data_sheet_init (PsppireDataSheet *obj) G_CALLBACK (on_query_tooltip), NULL); g_signal_connect (obj, "button-press-event", G_CALLBACK (on_button_pressed), NULL); - g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL); - obj->builder = builder_new ("data-sheet.ui"); + g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL); - action = get_action_assert (obj->builder, "edit_clear-cases"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_cases), - obj); - gtk_action_set_sensitive (action, FALSE); g_signal_connect (pspp_sheet_view_get_selection (sheet_view), "changed", G_CALLBACK (on_selection_changed), NULL); - - action = get_action_assert (obj->builder, "edit_insert-case"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_case), - obj); - - action = get_action_assert (obj->builder, "edit_insert-variable"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable), - obj); - - action = get_action_assert (obj->builder, "edit_goto-case"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_goto_case), - 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); - - action = get_action_assert (obj->builder, "edit_find"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_find), obj); - - action = get_action_assert (obj->builder, "sort-up"); - g_signal_connect (action, "activate", G_CALLBACK (on_sort_up), obj); - - action = get_action_assert (obj->builder, "sort-down"); - g_signal_connect (action, "activate", G_CALLBACK (on_sort_down), obj); - } GtkWidget * @@ -1766,19 +1736,11 @@ refresh_model (PsppireDataSheet *data_sheet) if (data_sheet->data_store != NULL) { - PsppireEmptyListStore *model; - GtkAction *action; - int n_rows; - - n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1; - model = psppire_empty_list_store_new (n_rows); + int n_rows = psppire_data_store_get_case_count (data_sheet->data_store) + 1; + PsppireEmptyListStore *model = psppire_empty_list_store_new (n_rows); pspp_sheet_view_set_model (PSPP_SHEET_VIEW (data_sheet), GTK_TREE_MODEL (model)); g_object_unref (model); - - action = get_action_assert (data_sheet->builder, "edit_copy"); - g_signal_connect (action, "activate", G_CALLBACK (on_edit_copy), - data_sheet); } } @@ -1883,6 +1845,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 +1858,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 +1987,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 +2004,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 +2032,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 +2086,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); - psppire_data_sheet_update_clipboard (data_sheet); + clip_datasheet = reader; + clip_dict = dict; + + psppire_data_sheet_update_clipboard (data_sheet); + } } enum { @@ -2160,14 +2136,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 +2152,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 +2161,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 +2178,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 +2197,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 +2207,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 +2225,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) @@ -2318,21 +2304,64 @@ static void psppire_data_sheet_update_clip_actions (PsppireDataSheet *data_sheet) { struct range_set *rows, *cols; - GtkAction *action; - gboolean enable; + 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); - enable = psppire_data_sheet_get_selected_range (data_sheet, &rows, &cols); if (enable) { range_set_destroy (rows); range_set_destroy (cols); } - action = get_action_assert (data_sheet->builder, "edit_copy"); - gtk_action_set_sensitive (action, enable); + 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); - action = get_action_assert (data_sheet->builder, "edit_cut"); - gtk_action_set_sensitive (action, enable); + 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. */ @@ -2349,13 +2378,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 +2398,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 +2430,42 @@ 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); + GtkWidget *mi = GTK_WIDGET (data); gboolean compatible_target = FALSE; - GtkAction *action; gint i; - 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); + 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; + + PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top); + + gtk_clipboard_request_targets (clip, + psppire_data_sheet_targets_received_cb, + dw->mi_paste); }