From 3358031c44f487dde08fddf3ebf9200eb9087d7e Mon Sep 17 00:00:00 2001 From: John Darrington Date: Wed, 18 Jul 2007 05:32:21 +0000 Subject: [PATCH] Added popup menus to column and row title areas in datasheet. --- lib/gtksheet/gtksheet.c | 142 ++++++++++++++------- lib/gtksheet/gtksheet.h | 2 +- src/ui/gui/data-editor.c | 215 +++++++++++++++++++++++++++++++- src/ui/gui/data-editor.h | 7 +- src/ui/gui/psppire-case-file.c | 3 + src/ui/gui/psppire-case-file.h | 2 +- src/ui/gui/psppire-data-store.c | 2 + 7 files changed, 327 insertions(+), 46 deletions(-) diff --git a/lib/gtksheet/gtksheet.c b/lib/gtksheet/gtksheet.c index 72115b89..05cc5fd4 100644 --- a/lib/gtksheet/gtksheet.c +++ b/lib/gtksheet/gtksheet.c @@ -797,6 +797,8 @@ enum SELECT_COLUMN, DOUBLE_CLICK_ROW, DOUBLE_CLICK_COLUMN, + BUTTON_EVENT_ROW, + BUTTON_EVENT_COLUMN, SELECT_RANGE, CLIP_RANGE, RESIZE_RANGE, @@ -934,7 +936,7 @@ gtk_sheet_class_init (GtkSheetClass * klass) /** - * GtkSheet::double - click - row + * GtkSheet::double-click-row * @sheet: the sheet widget that emitted the signal * @row: the row that was double clicked. * @@ -953,7 +955,7 @@ gtk_sheet_class_init (GtkSheetClass * klass) /** - * GtkSheet::double - click - column + * GtkSheet::double-click-column * @sheet: the sheet widget that emitted the signal * @column: the column that was double clicked. * @@ -971,6 +973,48 @@ gtk_sheet_class_init (GtkSheetClass * klass) G_TYPE_INT); + /** + * GtkSheet::button-event-column + * @sheet: the sheet widget that emitted the signal + * @column: the column on which the event occured. + * + * A button event occured on a column title button + */ + sheet_signals[BUTTON_EVENT_COLUMN] = + g_signal_new ("button-event-column", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + gtkextra_VOID__INT_POINTER, + G_TYPE_NONE, + 2, + G_TYPE_INT, + G_TYPE_POINTER + ); + + + /** + * GtkSheet::button-event-row + * @sheet: the sheet widget that emitted the signal + * @column: the column on which the event occured. + * + * A button event occured on a row title button + */ + sheet_signals[BUTTON_EVENT_ROW] = + g_signal_new ("button-event-row", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + gtkextra_VOID__INT_POINTER, + G_TYPE_NONE, + 2, + G_TYPE_INT, + G_TYPE_POINTER + ); + + sheet_signals[SELECT_RANGE] = g_signal_new ("select-range", G_TYPE_FROM_CLASS (object_class), @@ -1209,7 +1253,7 @@ gtk_sheet_init (GtkSheet *sheet) gdk_color_alloc (gdk_colormap_get_system (), &sheet->grid_color); sheet->show_grid = TRUE; - sheet->motion_events = 0; + sheet->motion_timer = 0; } @@ -4879,21 +4923,37 @@ gtk_sheet_button_press (GtkWidget * widget, sheet = GTK_SHEET (widget); - if ( event->type == GDK_2BUTTON_PRESS) + /* Cancel any pending tooltips */ + if (sheet->motion_timer) { - gtk_widget_get_pointer (widget, &x, &y); - gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); + g_source_remove (sheet->motion_timer); + sheet->motion_timer = 0; + } - if (event->window == sheet->column_title_window) - { - g_signal_emit (G_OBJECT (sheet), - sheet_signals[DOUBLE_CLICK_COLUMN], 0, column); - } - else if (event->window == sheet->row_title_window) - { - g_signal_emit (G_OBJECT (sheet), - sheet_signals[DOUBLE_CLICK_ROW], 0, row); - } + gtk_widget_get_pointer (widget, &x, &y); + gtk_sheet_get_pixel_info (sheet, x, y, &row, &column); + + + if (event->window == sheet->column_title_window) + { + g_signal_emit (G_OBJECT (sheet), + sheet_signals[BUTTON_EVENT_COLUMN], 0, + column, event); + + if ( event->type == GDK_2BUTTON_PRESS && event->button == 1) + g_signal_emit (G_OBJECT (sheet), + sheet_signals[DOUBLE_CLICK_COLUMN], 0, column); + + } + else if (event->window == sheet->row_title_window) + { + g_signal_emit (G_OBJECT (sheet), + sheet_signals[BUTTON_EVENT_ROW], 0, + row, event); + + if ( event->type == GDK_2BUTTON_PRESS && event->button == 1) + g_signal_emit (G_OBJECT (sheet), + sheet_signals[DOUBLE_CLICK_ROW], 0, row); } @@ -5422,39 +5482,36 @@ static gboolean motion_timeout_callback (gpointer data) { GtkSheet *sheet = GTK_SHEET (data); - if ( --sheet->motion_events == 0 ) + gint x, y; + gint row, column; + gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y); + + if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) ) { - gint x, y; - gint row, column; - gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y); + if ( column == -1 && row == -1 ) + return FALSE; - if ( gtk_sheet_get_pixel_info (sheet, x, y, &row, &column) ) + if ( column == -1) { - if ( column == -1 && row == -1 ) - return FALSE; + GSheetRow *row_geo = sheet->row_geometry; + gchar *text; - if ( column == -1) - { - GSheetRow *row_geo = sheet->row_geometry; - gchar *text; - - text = g_sheet_row_get_subtitle (row_geo, row); + text = g_sheet_row_get_subtitle (row_geo, row); - show_subtitle (sheet, row, column, text); - g_free (text); - } + show_subtitle (sheet, row, column, text); + g_free (text); + } - if ( row == -1) - { - GSheetColumn *col_geo = sheet->column_geometry; - gchar *text; + if ( row == -1) + { + GSheetColumn *col_geo = sheet->column_geometry; + gchar *text; - text = g_sheet_column_get_subtitle (col_geo, column); + text = g_sheet_column_get_subtitle (col_geo, column); - show_subtitle (sheet, row, column, text ); + show_subtitle (sheet, row, column, text ); - g_free (text); - } + g_free (text); } } @@ -5483,8 +5540,9 @@ gtk_sheet_motion (GtkWidget * widget, if (!sheet->hover_window || ! GTK_WIDGET_VISIBLE (sheet->hover_window->window)) { - sheet->motion_events++; - g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet); + if ( sheet->motion_timer > 0 ) + g_source_remove (sheet->motion_timer); + sheet->motion_timer = g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet); } else { diff --git a/lib/gtksheet/gtksheet.h b/lib/gtksheet/gtksheet.h index efce203b..c77eb472 100644 --- a/lib/gtksheet/gtksheet.h +++ b/lib/gtksheet/gtksheet.h @@ -228,7 +228,7 @@ struct _GtkSheet{ GtkSheetRange clip_range; /* Used for the subtitle (popups) */ - gint motion_events; + gint motion_timer; GtkSheetHoverTitle *hover_window; }; diff --git a/src/ui/gui/data-editor.c b/src/ui/gui/data-editor.c index 2e44c67c..28f15e01 100644 --- a/src/ui/gui/data-editor.c +++ b/src/ui/gui/data-editor.c @@ -51,6 +51,15 @@ #include "psppire-var-store.h" +static void create_data_sheet_variable_popup_menu (struct data_editor *); +static void create_data_sheet_cases_popup_menu (struct data_editor *); + +static void popup_variable_menu (GtkSheet *, gint, + GdkEventButton *, gpointer data); + +static void popup_cases_menu (GtkSheet *, gint, + GdkEventButton *, gpointer data); + /* Update the data_ref_entry with the reference of the active cell */ static gint update_data_ref_entry (const GtkSheet *sheet, gint row, gint col, gpointer data); @@ -609,6 +618,16 @@ new_data_editor (void) G_CALLBACK (minimise_all_windows), NULL); + create_data_sheet_variable_popup_menu (de); + create_data_sheet_cases_popup_menu (de); + + g_signal_connect (G_OBJECT (data_sheet), "button-event-column", + G_CALLBACK (popup_variable_menu), de); + + g_signal_connect (G_OBJECT (data_sheet), "button-event-row", + G_CALLBACK (popup_cases_menu), de); + + select_sheet (de, PAGE_DATA_SHEET); return de; @@ -1022,7 +1041,8 @@ on_split_change (PsppireDict *dict, gpointer data) { gint i; GString *text; - const struct variable *const * split_vars = dict_get_split_vars (dict->dict); + const struct variable *const * split_vars = + dict_get_split_vars (dict->dict); text = g_string_new (_("Split by ")); @@ -1471,3 +1491,196 @@ update_data_ref_entry (const GtkSheet *sheet, gint row, gint col, gpointer data) return FALSE; } + + + + + +static void +do_sort (PsppireDataStore *ds, int var, gboolean descend) +{ + GString *string = g_string_new ("SORT CASES BY "); + + const struct variable *v = + psppire_dict_get_variable (ds->dict, var); + + g_string_append_printf (string, "%s", var_get_name (v)); + + if ( descend ) + g_string_append (string, " (D)"); + + g_string_append (string, "."); + + execute_syntax (create_syntax_string_source (string->str)); + + g_string_free (string, TRUE); +} + + +static void +sort_up (GtkMenuItem *item, gpointer data) +{ + GtkSheet *sheet = data; + GtkSheetRange range; + gtk_sheet_get_selected_range (sheet, &range); + + do_sort (PSPPIRE_DATA_STORE (gtk_sheet_get_model(sheet)), + range.col0, FALSE); + +} + +static void +sort_down (GtkMenuItem *item, gpointer data) +{ + GtkSheet *sheet = data; + GtkSheetRange range; + gtk_sheet_get_selected_range (sheet, &range); + + do_sort (PSPPIRE_DATA_STORE (gtk_sheet_get_model(sheet)), + range.col0, TRUE); +} + + + + +static void +create_data_sheet_variable_popup_menu (struct data_editor *de) +{ + GtkSheet *sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet")); + GtkWidget *menu = gtk_menu_new (); + + GtkWidget *sort_ascending = + gtk_menu_item_new_with_label (_("Sort Ascending")); + + GtkWidget *sort_descending = + gtk_menu_item_new_with_label (_("Sort Descending")); + + + GtkWidget *insert_variable = + gtk_menu_item_new_with_label (_("Insert Variable")); + + GtkWidget *clear_variable = + gtk_menu_item_new_with_label (_("Clear")); + + + gtk_action_connect_proxy (de->insert_variable, + insert_variable ); + + + gtk_action_connect_proxy (de->delete_variables, + clear_variable ); + + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_variable); + + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), + gtk_separator_menu_item_new ()); + + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), clear_variable); + + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), + gtk_separator_menu_item_new ()); + + + g_signal_connect (G_OBJECT (sort_ascending), "activate", + G_CALLBACK (sort_up), sheet); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_ascending); + + + g_signal_connect (G_OBJECT (sort_descending), "activate", + G_CALLBACK (sort_down), sheet); + + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_descending); + + gtk_widget_show_all (menu); + + + de->data_sheet_variable_popup_menu = GTK_MENU(menu); +} + + +static void +create_data_sheet_cases_popup_menu (struct data_editor *de) +{ + GtkWidget *menu = gtk_menu_new (); + + GtkWidget *insert_case = + gtk_menu_item_new_with_label (_("Insert Case")); + + GtkWidget *delete_case = + gtk_menu_item_new_with_label (_("Clear")); + + + gtk_action_connect_proxy (de->insert_case, + insert_case); + + + gtk_action_connect_proxy (de->delete_cases, + delete_case); + + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_case); + + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), + gtk_separator_menu_item_new ()); + + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), delete_case); + + + gtk_widget_show_all (menu); + + + de->data_sheet_cases_popup_menu = GTK_MENU (menu); +} + + +static void +popup_variable_menu (GtkSheet *sheet, gint column, + GdkEventButton *event, gpointer data) +{ + struct data_editor *de = data; + + PsppireDataStore *data_store = + PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet)); + + const struct variable *v = + psppire_dict_get_variable (data_store->dict, column); + + if ( v && event->button == 3) + { + + gtk_sheet_select_column (sheet, column); + + gtk_menu_popup (GTK_MENU (de->data_sheet_variable_popup_menu), + NULL, NULL, NULL, NULL, + event->button, event->time); + } +} + + +static void +popup_cases_menu (GtkSheet *sheet, gint row, + GdkEventButton *event, gpointer data) +{ + struct data_editor *de = data; + + PsppireDataStore *data_store = + PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet)); + + if ( row <= psppire_data_store_get_case_count (data_store) && + event->button == 3) + { + gtk_sheet_select_row (sheet, row); + + gtk_menu_popup (GTK_MENU (de->data_sheet_cases_popup_menu), + NULL, NULL, NULL, NULL, + event->button, event->time); + } +} diff --git a/src/ui/gui/data-editor.h b/src/ui/gui/data-editor.h index 582ae227..930423ee 100644 --- a/src/ui/gui/data-editor.h +++ b/src/ui/gui/data-editor.h @@ -31,6 +31,7 @@ struct data_editor GtkAction *action_data_save_as; GtkAction *action_data_save; + /* Actions which invoke dialog boxes */ GtkAction *invoke_weight_cases_dialog; GtkAction *invoke_transpose_dialog; GtkAction *invoke_split_file_dialog; @@ -40,14 +41,18 @@ struct data_editor GtkAction *invoke_goto_dialog; GtkAction *invoke_variable_info_dialog; + /* Actions which do things */ GtkAction *insert_variable; GtkAction *insert_case; - GtkAction *delete_variables; GtkAction *delete_cases; GladeXML *xml; + GtkMenu *data_sheet_variable_popup_menu; + GtkMenu *data_sheet_cases_popup_menu; + + gboolean save_as_portable; /* Name of the file this data is associated with (ie, was loaded from or diff --git a/src/ui/gui/psppire-case-file.c b/src/ui/gui/psppire-case-file.c index 878d7fa0..a44309ee 100644 --- a/src/ui/gui/psppire-case-file.c +++ b/src/ui/gui/psppire-case-file.c @@ -169,6 +169,9 @@ psppire_case_file_delete_cases (PsppireCaseFile *cf, casenumber n_cases, casenum g_return_val_if_fail (cf, FALSE); g_return_val_if_fail (cf->datasheet, FALSE); + g_return_val_if_fail (first + n_cases <= + psppire_case_file_get_case_count (cf), FALSE); + datasheet_delete_rows (cf->datasheet, first, n_cases); g_signal_emit (cf, signals [CASES_DELETED], 0, first, n_cases); diff --git a/src/ui/gui/psppire-case-file.h b/src/ui/gui/psppire-case-file.h index 1dc297ac..ff00cb80 100644 --- a/src/ui/gui/psppire-case-file.h +++ b/src/ui/gui/psppire-case-file.h @@ -71,7 +71,7 @@ PsppireCaseFile *psppire_case_file_new (const struct casereader *); gboolean psppire_case_file_insert_case (PsppireCaseFile *cf, struct ccase *c, casenumber row); -casenumber psppire_case_file_get_case_count (const PsppireCaseFile *cf); +inline casenumber psppire_case_file_get_case_count (const PsppireCaseFile *cf); union value * psppire_case_file_get_value (const PsppireCaseFile *cf, diff --git a/src/ui/gui/psppire-data-store.c b/src/ui/gui/psppire-data-store.c index cc755761..323196e4 100644 --- a/src/ui/gui/psppire-data-store.c +++ b/src/ui/gui/psppire-data-store.c @@ -482,6 +482,8 @@ psppire_data_store_insert_new_case (PsppireDataStore *ds, casenumber posn) g_return_val_if_fail (val_cnt > 0, FALSE); + g_return_val_if_fail (posn <= psppire_data_store_get_case_count (ds), FALSE); + case_create (&cc, val_cnt); memset ( case_data_rw_idx (&cc, 0), 0, val_cnt * MAX_SHORT_STRING); -- 2.30.2