X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fgui%2Fpspp-sheet-view.c;h=9dbec5916b364507f24069e1fb7bf8b34010775e;hb=9d1bfb34842de4a129140622ee3d800297c0e69d;hp=1e50c2801a62c41dfcb1fe9166d6e81b35114ded;hpb=5d346786ca6b5fd5b9ecc4d1b7b16c5fd4daace8;p=pspp diff --git a/src/ui/gui/pspp-sheet-view.c b/src/ui/gui/pspp-sheet-view.c index 1e50c2801a..9dbec5916b 100644 --- a/src/ui/gui/pspp-sheet-view.c +++ b/src/ui/gui/pspp-sheet-view.c @@ -143,7 +143,10 @@ enum { PROP_HOVER_SELECTION, PROP_RUBBER_BANDING, PROP_ENABLE_GRID_LINES, - PROP_TOOLTIP_COLUMN + PROP_TOOLTIP_COLUMN, + PROP_SPECIAL_CELLS, + PROP_FIXED_HEIGHT, + PROP_FIXED_HEIGHT_SET }; /* object signals */ @@ -157,8 +160,7 @@ static void pspp_sheet_view_get_property (GObject *object, GValue *value, GParamSpec *pspec); -/* gtkobject signals */ -static void pspp_sheet_view_destroy (GtkObject *object); +static void pspp_sheet_view_dispose (GObject *object); /* gtkwidget signals */ static void pspp_sheet_view_realize (GtkWidget *widget); @@ -313,12 +315,14 @@ static void pspp_sheet_view_clamp_column_visible (PsppSheetView static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view, GdkEventMotion *event); static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view); -static void pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view, +static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view, gint count); static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view, gint count); static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view, gint count); +static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view, + gint count); static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view, gint count); static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view, @@ -385,6 +389,10 @@ static void pspp_sheet_view_put (PsppSheetView *t gint height); static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view, GtkTreePath *cursor_path); +static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *, + GdkEventButton *, + PsppSheetView *); +static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *); static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view, PsppSheetViewColumn *column, GtkTreePath *path, @@ -392,14 +400,18 @@ static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view, GdkRectangle *cell_area, GdkEvent *event, guint flags); -static void pspp_sheet_view_stop_editing (PsppSheetView *tree_view, - gboolean cancel_editing); static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view, gboolean keybinding); static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view); static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view, PsppSheetViewColumn *column, gint drop_position); +static void +pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view, + PsppSheetViewColumn *column, + const GdkRectangle *background_area, + gboolean subtract_focus_rect, + GdkRectangle *cell_area); static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view, gint height, int *new_node); @@ -418,6 +430,8 @@ static void remove_scroll_timeout (PsppSheetView *tree_view); static guint tree_view_signals [LAST_SIGNAL] = { 0 }; +static GtkBindingSet *edit_bindings; + /* GType Methods @@ -431,15 +445,17 @@ static void pspp_sheet_view_class_init (PsppSheetViewClass *class) { GObjectClass *o_class; - GtkObjectClass *object_class; GtkWidgetClass *widget_class; GtkContainerClass *container_class; - GtkBindingSet *binding_set; + GtkBindingSet *binding_set[2]; + int i; - binding_set = gtk_binding_set_by_class (class); + binding_set[0] = gtk_binding_set_by_class (class); + + binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing"); + edit_bindings = binding_set[1]; o_class = (GObjectClass *) class; - object_class = (GtkObjectClass *) class; widget_class = (GtkWidgetClass *) class; container_class = (GtkContainerClass *) class; @@ -447,9 +463,7 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class) o_class->set_property = pspp_sheet_view_set_property; o_class->get_property = pspp_sheet_view_get_property; o_class->finalize = pspp_sheet_view_finalize; - - /* GtkObject signals */ - object_class->destroy = pspp_sheet_view_destroy; + o_class->dispose = pspp_sheet_view_dispose; /* GtkWidget signals */ widget_class->map = pspp_sheet_view_map; @@ -577,7 +591,7 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class) * Enables of disables the hover selection mode of @tree_view. * Hover selection makes the selected row follow the pointer. * Currently, this works only for the selection modes - * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE. + * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE. * * This mode is primarily intended for treeviews in popups, e.g. * in #GtkComboBox or #GtkEntryCompletion. @@ -619,6 +633,33 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class) -1, GTK_PARAM_READWRITE)); + g_object_class_install_property (o_class, + PROP_SPECIAL_CELLS, + g_param_spec_enum ("special-cells", + P_("Special Cells"), + P_("Whether rows have special cells."), + PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS, + PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (o_class, + PROP_FIXED_HEIGHT, + g_param_spec_int ("fixed-height", + P_("Fixed Height"), + P_("Height of a single row. Normally the height of a row is determined automatically. Writing this property sets fixed-height-set to true, preventing this property's value from changing."), + -1, + G_MAXINT, + -1, + GTK_PARAM_READWRITE)); + + g_object_class_install_property (o_class, + PROP_FIXED_HEIGHT_SET, + g_param_spec_boolean ("fixed-height-set", + P_("Fixed Height Set"), + P_("Whether fixed-height was set externally."), + FALSE, + GTK_PARAM_READWRITE)); + /* Style properties */ #define _TREE_VIEW_EXPANDER_SIZE 12 #define _TREE_VIEW_VERTICAL_SEPARATOR 2 @@ -779,7 +820,7 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class) tree_view_signals[MOVE_CURSOR] = g_signal_new ("move-cursor", - G_TYPE_FROM_CLASS (object_class), + G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor), NULL, NULL, @@ -790,7 +831,7 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class) tree_view_signals[SELECT_ALL] = g_signal_new ("select-all", - G_TYPE_FROM_CLASS (object_class), + G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (PsppSheetViewClass, select_all), NULL, NULL, @@ -799,7 +840,7 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class) tree_view_signals[UNSELECT_ALL] = g_signal_new ("unselect-all", - G_TYPE_FROM_CLASS (object_class), + G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all), NULL, NULL, @@ -808,7 +849,7 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class) tree_view_signals[SELECT_CURSOR_ROW] = g_signal_new ("select-cursor-row", - G_TYPE_FROM_CLASS (object_class), + G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row), NULL, NULL, @@ -818,7 +859,7 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class) tree_view_signals[TOGGLE_CURSOR_ROW] = g_signal_new ("toggle-cursor-row", - G_TYPE_FROM_CLASS (object_class), + G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row), NULL, NULL, @@ -827,7 +868,7 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class) tree_view_signals[START_INTERACTIVE_SEARCH] = g_signal_new ("start-interactive-search", - G_TYPE_FROM_CLASS (object_class), + G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search), NULL, NULL, @@ -835,110 +876,121 @@ pspp_sheet_view_class_init (PsppSheetViewClass *class) G_TYPE_BOOLEAN, 0); /* Key bindings */ - pspp_sheet_view_add_move_binding (binding_set, GDK_Up, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, -1); - pspp_sheet_view_add_move_binding (binding_set, GDK_KP_Up, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, -1); + for (i = 0; i < 2; i++) + { + pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE, + GTK_MOVEMENT_DISPLAY_LINES, -1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE, + GTK_MOVEMENT_DISPLAY_LINES, -1); - pspp_sheet_view_add_move_binding (binding_set, GDK_Down, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, 1); - pspp_sheet_view_add_move_binding (binding_set, GDK_KP_Down, 0, TRUE, - GTK_MOVEMENT_DISPLAY_LINES, 1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE, + GTK_MOVEMENT_DISPLAY_LINES, 1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE, + GTK_MOVEMENT_DISPLAY_LINES, 1); - pspp_sheet_view_add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK, FALSE, - GTK_MOVEMENT_DISPLAY_LINES, -1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE, + GTK_MOVEMENT_DISPLAY_LINES, -1); - pspp_sheet_view_add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK, FALSE, - GTK_MOVEMENT_DISPLAY_LINES, 1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE, + GTK_MOVEMENT_DISPLAY_LINES, 1); - pspp_sheet_view_add_move_binding (binding_set, GDK_Home, 0, TRUE, - GTK_MOVEMENT_BUFFER_ENDS, -1); - pspp_sheet_view_add_move_binding (binding_set, GDK_KP_Home, 0, TRUE, - GTK_MOVEMENT_BUFFER_ENDS, -1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE, + GTK_MOVEMENT_BUFFER_ENDS, -1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE, + GTK_MOVEMENT_BUFFER_ENDS, -1); - pspp_sheet_view_add_move_binding (binding_set, GDK_End, 0, TRUE, - GTK_MOVEMENT_BUFFER_ENDS, 1); - pspp_sheet_view_add_move_binding (binding_set, GDK_KP_End, 0, TRUE, - GTK_MOVEMENT_BUFFER_ENDS, 1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE, + GTK_MOVEMENT_BUFFER_ENDS, 1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE, + GTK_MOVEMENT_BUFFER_ENDS, 1); - pspp_sheet_view_add_move_binding (binding_set, GDK_Page_Up, 0, TRUE, - GTK_MOVEMENT_PAGES, -1); - pspp_sheet_view_add_move_binding (binding_set, GDK_KP_Page_Up, 0, TRUE, - GTK_MOVEMENT_PAGES, -1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE, + GTK_MOVEMENT_PAGES, -1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE, + GTK_MOVEMENT_PAGES, -1); - pspp_sheet_view_add_move_binding (binding_set, GDK_Page_Down, 0, TRUE, - GTK_MOVEMENT_PAGES, 1); - pspp_sheet_view_add_move_binding (binding_set, GDK_KP_Page_Down, 0, TRUE, - GTK_MOVEMENT_PAGES, 1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE, + GTK_MOVEMENT_PAGES, 1); + pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE, + GTK_MOVEMENT_PAGES, 1); - gtk_binding_entry_add_signal (binding_set, GDK_Right, 0, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, 1); + gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_Left, 0, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, -1); + gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, 0, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, 1); + gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS, + G_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, 0, "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, -1); + gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS, + G_TYPE_INT, -1); - gtk_binding_entry_add_signal (binding_set, GDK_Right, GDK_CONTROL_MASK, - "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, 1); + gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_Left, GDK_CONTROL_MASK, - "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, -1); + gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, GDK_CONTROL_MASK, - "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, 1); + gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK, + "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, GDK_CONTROL_MASK, - "move-cursor", 2, - G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, - G_TYPE_INT, -1); + gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK, + "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); - gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0); + gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK, + "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, 1); - gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK, "select-all", 0); - gtk_binding_entry_add_signal (binding_set, GDK_slash, GDK_CONTROL_MASK, "select-all", 0); + gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK, + "move-cursor", 2, + G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS, + G_TYPE_INT, -1); - gtk_binding_entry_add_signal (binding_set, GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0); - gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0); + gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0); - gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1, + gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0); + } + + gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0); + gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0); + + gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0); + gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0); + + gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0); + gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0); + + gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1, G_TYPE_BOOLEAN, TRUE); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1, + gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1, G_TYPE_BOOLEAN, TRUE); - gtk_binding_entry_add_signal (binding_set, GDK_space, 0, "select-cursor-row", 1, + gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1, G_TYPE_BOOLEAN, TRUE); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Space, 0, "select-cursor-row", 1, + gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1, G_TYPE_BOOLEAN, TRUE); - gtk_binding_entry_add_signal (binding_set, GDK_Return, 0, "select-cursor-row", 1, + gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1, G_TYPE_BOOLEAN, TRUE); - gtk_binding_entry_add_signal (binding_set, GDK_ISO_Enter, 0, "select-cursor-row", 1, + gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1, G_TYPE_BOOLEAN, TRUE); - gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0, "select-cursor-row", 1, + gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1, G_TYPE_BOOLEAN, TRUE); - gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0, "select-cursor-parent", 0); - gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0); - - gtk_binding_entry_add_signal (binding_set, GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0); - - gtk_binding_entry_add_signal (binding_set, GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0); + gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0); + gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0); g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate)); } @@ -976,6 +1028,7 @@ pspp_sheet_view_init (PsppSheetView *tree_view) tree_view->priv->presize_handler_timer = 0; tree_view->priv->scroll_sync_timer = 0; tree_view->priv->fixed_height = -1; + tree_view->priv->fixed_height_set = FALSE; pspp_sheet_view_set_adjustments (tree_view, NULL, NULL); tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view); tree_view->priv->enable_search = TRUE; @@ -995,6 +1048,8 @@ pspp_sheet_view_init (PsppSheetView *tree_view) tree_view->priv->tooltip_column = -1; + tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT; + tree_view->priv->post_validation_flag = FALSE; tree_view->priv->last_button_x = -1; @@ -1006,6 +1061,12 @@ pspp_sheet_view_init (PsppSheetView *tree_view) tree_view->priv->prelight_node = -1; tree_view->priv->rubber_band_start_node = -1; tree_view->priv->rubber_band_end_node = -1; + + tree_view->priv->anchor_column = NULL; + + tree_view->priv->button_style = NULL; + + tree_view->dispose_has_run = FALSE; } @@ -1064,6 +1125,32 @@ pspp_sheet_view_set_property (GObject *object, case PROP_TOOLTIP_COLUMN: pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value)); break; + case PROP_SPECIAL_CELLS: + pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value)); + break; + case PROP_FIXED_HEIGHT: + pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value)); + break; + case PROP_FIXED_HEIGHT_SET: + if (g_value_get_boolean (value)) + { + if (!tree_view->priv->fixed_height_set + && tree_view->priv->fixed_height >= 0) + { + tree_view->priv->fixed_height_set = true; + g_object_notify (G_OBJECT (tree_view), "fixed-height-set"); + } + } + else + { + if (tree_view->priv->fixed_height_set) + { + tree_view->priv->fixed_height_set = false; + g_object_notify (G_OBJECT (tree_view), "fixed-height-set"); + install_presize_handler (tree_view); + } + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1121,6 +1208,15 @@ pspp_sheet_view_get_property (GObject *object, case PROP_TOOLTIP_COLUMN: g_value_set_int (value, tree_view->priv->tooltip_column); break; + case PROP_SPECIAL_CELLS: + g_value_set_enum (value, tree_view->priv->special_cells); + break; + case PROP_FIXED_HEIGHT: + g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view)); + break; + case PROP_FIXED_HEIGHT_SET: + g_value_set_boolean (value, tree_view->priv->fixed_height_set); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1128,9 +1224,41 @@ pspp_sheet_view_get_property (GObject *object, } static void -pspp_sheet_view_finalize (GObject *object) +pspp_sheet_view_dispose (GObject *object) { - G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object); + PsppSheetView *tree_view = PSPP_SHEET_VIEW (object); + + if (tree_view->dispose_has_run) + return; + + tree_view->dispose_has_run = TRUE; + + if (tree_view->priv->selection != NULL) + { + _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL); + g_object_unref (tree_view->priv->selection); + tree_view->priv->selection = NULL; + } + + if (tree_view->priv->hadjustment) + { + g_object_unref (tree_view->priv->hadjustment); + tree_view->priv->hadjustment = NULL; + } + if (tree_view->priv->vadjustment) + { + g_object_unref (tree_view->priv->vadjustment); + tree_view->priv->vadjustment = NULL; + } + + if (tree_view->priv->button_style) + { + g_object_unref (tree_view->priv->button_style); + tree_view->priv->button_style = NULL; + } + + + G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object); } @@ -1144,14 +1272,10 @@ pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view, pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child)); } -/* GtkObject Methods - */ - static void -pspp_sheet_view_destroy (GtkObject *object) +pspp_sheet_view_finalize (GObject *object) { PsppSheetView *tree_view = PSPP_SHEET_VIEW (object); - GList *list; pspp_sheet_view_stop_editing (tree_view, TRUE); @@ -1161,27 +1285,9 @@ pspp_sheet_view_destroy (GtkObject *object) tree_view->priv->selected = NULL; } - if (tree_view->priv->columns != NULL) - { - list = tree_view->priv->columns; - while (list) - { - PsppSheetViewColumn *column; - column = PSPP_SHEET_VIEW_COLUMN (list->data); - list = list->next; - pspp_sheet_view_remove_column (tree_view, column); - } - tree_view->priv->columns = NULL; - } tree_view->priv->prelight_node = -1; - if (tree_view->priv->selection != NULL) - { - _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL); - g_object_unref (tree_view->priv->selection); - tree_view->priv->selection = NULL; - } if (tree_view->priv->scroll_to_path != NULL) { @@ -1248,18 +1354,8 @@ pspp_sheet_view_destroy (GtkObject *object) pspp_sheet_view_set_model (tree_view, NULL); - if (tree_view->priv->hadjustment) - { - g_object_unref (tree_view->priv->hadjustment); - tree_view->priv->hadjustment = NULL; - } - if (tree_view->priv->vadjustment) - { - g_object_unref (tree_view->priv->vadjustment); - tree_view->priv->vadjustment = NULL; - } - GTK_OBJECT_CLASS (pspp_sheet_view_parent_class)->destroy (object); + G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object); } @@ -1282,14 +1378,15 @@ pspp_sheet_view_map_buttons (PsppSheetView *tree_view) for (list = tree_view->priv->columns; list; list = list->next) { column = list->data; - if (gtk_widget_get_visible (column->button) && + if (column->button != NULL && + gtk_widget_get_visible (column->button) && !gtk_widget_get_mapped (column->button)) gtk_widget_map (column->button); } for (list = tree_view->priv->columns; list; list = list->next) { column = list->data; - if (column->visible == FALSE) + if (column->visible == FALSE || column->window == NULL) continue; if (column->resizable) { @@ -1333,6 +1430,7 @@ pspp_sheet_view_map (GtkWidget *widget) static void pspp_sheet_view_realize (GtkWidget *widget) { + gint i; PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); GList *tmp_list; GdkWindowAttr attributes; @@ -1414,15 +1512,24 @@ pspp_sheet_view_realize (GtkWidget *widget) pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines); install_presize_handler (tree_view); + + for (i = 0; i < 5; ++i) + { + tree_view->priv->grid_line_gc[i] = gdk_gc_new (widget->window); + gdk_gc_copy (tree_view->priv->grid_line_gc[i], widget->style->text_aa_gc[i]); + } } static void pspp_sheet_view_unrealize (GtkWidget *widget) { + gint x; PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); PsppSheetViewPrivate *priv = tree_view->priv; GList *list; + GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget); + if (priv->scroll_timeout != 0) { g_source_remove (priv->scroll_timeout); @@ -1484,13 +1591,21 @@ pspp_sheet_view_unrealize (GtkWidget *widget) priv->drag_highlight_window = NULL; } - if (priv->grid_line_gc) + for (x = 0 ; x < 5 ; ++x) + g_object_unref (priv->grid_line_gc[x]); + + if (tree_view->priv->columns != NULL) { - g_object_unref (priv->grid_line_gc); - priv->grid_line_gc = NULL; + list = tree_view->priv->columns; + while (list) + { + PsppSheetViewColumn *column; + column = PSPP_SHEET_VIEW_COLUMN (list->data); + list = list->next; + pspp_sheet_view_remove_column (tree_view, column); + } + tree_view->priv->columns = NULL; } - - GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget); } /* GtkWidget::size_request helper */ @@ -1508,12 +1623,7 @@ pspp_sheet_view_size_request_columns (PsppSheetView *tree_view) GtkRequisition requisition; PsppSheetViewColumn *column = list->data; - if (column->button == NULL) - continue; - - column = list->data; - - gtk_widget_size_request (column->button, &requisition); + pspp_sheet_view_column_size_request (column, &requisition); column->button_request = requisition.width; tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height); } @@ -1677,6 +1787,15 @@ pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_ return real_requested_width; } +static gboolean +span_intersects (int a0, int a_width, + int b0, int b_width) +{ + int a1 = a0 + a_width; + int b1 = b0 + b_width; + return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1); +} + /* GtkWidget::size_allocate helper */ static void pspp_sheet_view_size_allocate_columns (GtkWidget *widget, @@ -1687,13 +1806,12 @@ pspp_sheet_view_size_allocate_columns (GtkWidget *widget, PsppSheetViewColumn *column; GtkAllocation allocation; gint width = 0; - gint extra, extra_per_column, extra_for_last; + gint extra, extra_per_column; gint full_requested_width = 0; gint number_of_expand_columns = 0; gboolean column_changed = FALSE; gboolean rtl; - gboolean update_expand; - + tree_view = PSPP_SHEET_VIEW (widget); for (last_column = g_list_last (tree_view->priv->columns); @@ -1727,42 +1845,12 @@ pspp_sheet_view_size_allocate_columns (GtkWidget *widget, number_of_expand_columns++; } - /* Only update the expand value if the width of the widget has changed, - * or the number of expand columns has changed, or if there are no expand - * columns, or if we didn't have an size-allocation yet after the - * last validated node. - */ - update_expand = (width_changed && *width_changed == TRUE) - || number_of_expand_columns != tree_view->priv->last_number_of_expand_columns - || number_of_expand_columns == 0 - || tree_view->priv->post_validation_flag == TRUE; - - tree_view->priv->post_validation_flag = FALSE; - - if (!update_expand) - { - extra = tree_view->priv->last_extra_space; - extra_for_last = MAX (widget->allocation.width - full_requested_width - extra, 0); - } - else - { - extra = MAX (widget->allocation.width - full_requested_width, 0); - extra_for_last = 0; - - tree_view->priv->last_extra_space = extra; - } - + extra = MAX (widget->allocation.width - full_requested_width, 0); if (number_of_expand_columns > 0) extra_per_column = extra/number_of_expand_columns; else extra_per_column = 0; - if (update_expand) - { - tree_view->priv->last_extra_space_per_column = extra_per_column; - tree_view->priv->last_number_of_expand_columns = number_of_expand_columns; - } - for (list = (rtl ? last_column : first_column); list != (rtl ? first_column->prev : last_column->next); list = (rtl ? list->prev : list->next)) @@ -1786,8 +1874,8 @@ pspp_sheet_view_size_allocate_columns (GtkWidget *widget, &(drag_allocation.height)); drag_allocation.x = 0; drag_allocation.y = 0; - gtk_widget_size_allocate (tree_view->priv->drag_column->button, - &drag_allocation); + pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column, + &drag_allocation); width += drag_allocation.width; continue; } @@ -1812,19 +1900,9 @@ pspp_sheet_view_size_allocate_columns (GtkWidget *widget, number_of_expand_columns --; } } - else if (number_of_expand_columns == 0 && - list == last_column) - { - column->width += extra; - } - /* In addition to expand, the last column can get even more - * extra space so all available space is filled up. - */ - if (extra_for_last > 0 && list == last_column) - column->width += extra_for_last; - - g_object_notify (G_OBJECT (column), "width"); + if (column->width != old_width) + g_object_notify (G_OBJECT (column), "width"); allocation.width = column->width; width += column->width; @@ -1832,7 +1910,13 @@ pspp_sheet_view_size_allocate_columns (GtkWidget *widget, if (column->width > old_width) column_changed = TRUE; - gtk_widget_size_allocate (column->button, &allocation); + pspp_sheet_view_column_size_allocate (column, &allocation); + + if (span_intersects (allocation.x, allocation.width, + tree_view->priv->hadjustment->value, + widget->allocation.width) + && gtk_widget_get_realized (widget)) + pspp_sheet_view_column_set_need_button (column, TRUE); if (column->window) gdk_window_move_resize (column->window, @@ -2032,6 +2116,192 @@ pspp_sheet_view_node_prev (PsppSheetView *tree_view, return node > 0 ? node - 1 : -1; } +static gboolean +all_columns_selected (PsppSheetView *tree_view) +{ + GList *list; + + for (list = tree_view->priv->columns; list; list = list->next) + { + PsppSheetViewColumn *column = list->data; + if (column->selectable && !column->selected) + return FALSE; + } + + return TRUE; +} + +static gboolean +pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view, + gint node, + PsppSheetViewColumn *column, + GdkEventButton *event) +{ + PsppSheetSelection *selection; + PsppSheetSelectionMode mode; + GtkTreePath *path; + gboolean update_anchor; + gboolean handled; + guint modifiers; + + g_return_val_if_fail (tree_view != NULL, FALSE); + g_return_val_if_fail (column != NULL, FALSE); + + selection = tree_view->priv->selection; + mode = pspp_sheet_selection_get_mode (selection); + if (mode != PSPP_SHEET_SELECTION_RECTANGLE) + return FALSE; + + if (!column->row_head) + return FALSE; + + if (event) + { + modifiers = event->state & gtk_accelerator_get_default_mod_mask (); + if (event->type != GDK_BUTTON_PRESS + || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK)) + return FALSE; + } + else + modifiers = 0; + + path = gtk_tree_path_new_from_indices (node, -1); + if (event == NULL) + { + pspp_sheet_selection_unselect_all (selection); + pspp_sheet_selection_select_path (selection, path); + pspp_sheet_selection_select_all_columns (selection); + update_anchor = TRUE; + handled = TRUE; + } + else if (event->type == GDK_BUTTON_PRESS && event->button == 3) + { + if (pspp_sheet_selection_count_selected_rows (selection) <= 1 + || !all_columns_selected (tree_view)) + { + pspp_sheet_selection_unselect_all (selection); + pspp_sheet_selection_select_path (selection, path); + pspp_sheet_selection_select_all_columns (selection); + update_anchor = TRUE; + handled = FALSE; + } + else + update_anchor = handled = FALSE; + } + else if (event->type == GDK_BUTTON_PRESS && event->button == 1 + && modifiers == GDK_CONTROL_MASK) + { + if (!all_columns_selected (tree_view)) + { + pspp_sheet_selection_unselect_all (selection); + pspp_sheet_selection_select_all_columns (selection); + } + + if (pspp_sheet_selection_path_is_selected (selection, path)) + pspp_sheet_selection_unselect_path (selection, path); + else + pspp_sheet_selection_select_path (selection, path); + update_anchor = TRUE; + handled = TRUE; + } + else if (event->type == GDK_BUTTON_PRESS && event->button == 1 + && modifiers == GDK_SHIFT_MASK) + { + GtkTreeRowReference *anchor = tree_view->priv->anchor; + GtkTreePath *anchor_path; + + if (all_columns_selected (tree_view) + && gtk_tree_row_reference_valid (anchor)) + { + update_anchor = FALSE; + anchor_path = gtk_tree_row_reference_get_path (anchor); + } + else + { + update_anchor = TRUE; + anchor_path = gtk_tree_path_copy (path); + } + + pspp_sheet_selection_unselect_all (selection); + pspp_sheet_selection_select_range (selection, anchor_path, path); + pspp_sheet_selection_select_all_columns (selection); + + gtk_tree_path_free (anchor_path); + + handled = TRUE; + } + else + update_anchor = handled = FALSE; + + if (update_anchor) + { + if (tree_view->priv->anchor) + gtk_tree_row_reference_free (tree_view->priv->anchor); + tree_view->priv->anchor = + gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), + tree_view->priv->model, + path); + } + + gtk_tree_path_free (path); + return handled; +} + +static gboolean +find_click (PsppSheetView *tree_view, + gint x, gint y, + gint *node, + PsppSheetViewColumn **column, + GdkRectangle *background_area, + GdkRectangle *cell_area) +{ + gint y_offset; + gboolean rtl; + GList *list; + gint new_y; + + /* find the node that was clicked */ + new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y); + if (new_y < 0) + new_y = 0; + y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node); + + if (*node < 0) + return FALSE; + + background_area->y = y_offset + y; + background_area->height = ROW_HEIGHT (tree_view); + background_area->x = 0; + + /* Let the column have a chance at selecting it. */ + rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); + for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); + list; list = (rtl ? list->prev : list->next)) + { + PsppSheetViewColumn *candidate = list->data; + + if (!candidate->visible) + continue; + + background_area->width = candidate->width; + if ((background_area->x > x) || + (background_area->x + background_area->width <= x)) + { + background_area->x += background_area->width; + continue; + } + + /* we found the focus column */ + + pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area, + TRUE, cell_area); + *column = candidate; + return TRUE; + } + + return FALSE; +} + static gboolean pspp_sheet_view_button_press (GtkWidget *widget, GdkEventButton *event) @@ -2042,16 +2312,10 @@ pspp_sheet_view_button_press (GtkWidget *widget, gint i; GdkRectangle background_area; GdkRectangle cell_area; - gint vertical_separator; - gint horizontal_separator; gboolean rtl; rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); pspp_sheet_view_stop_editing (tree_view, FALSE); - gtk_widget_style_get (widget, - "vertical-separator", &vertical_separator, - "horizontal-separator", &horizontal_separator, - NULL); /* Because grab_focus can cause reentrancy, we delay grab_focus until after @@ -2062,154 +2326,36 @@ pspp_sheet_view_button_press (GtkWidget *widget, { int node; GtkTreePath *path; - gchar *path_string; - gint depth; - gint new_y; - gint y_offset; gint dval; gint pre_val, aft_val; PsppSheetViewColumn *column = NULL; GtkCellRenderer *focus_cell = NULL; - gint column_handled_click = FALSE; gboolean row_double_click = FALSE; - gboolean rtl; - gboolean node_selected; /* Empty tree? */ if (tree_view->priv->row_count == 0) { grab_focus_and_unset_draw_keyfocus (tree_view); return TRUE; - } - - /* find the node that was clicked */ - new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y); - if (new_y < 0) - new_y = 0; - y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node); - - if (node < 0) - { - /* We clicked in dead space */ - grab_focus_and_unset_draw_keyfocus (tree_view); - return TRUE; - } - - /* Get the path and the node */ - path = _pspp_sheet_view_find_path (tree_view, node); - - depth = gtk_tree_path_get_depth (path); - background_area.y = y_offset + event->y; - background_area.height = ROW_HEIGHT (tree_view); - background_area.x = 0; - - - /* Let the column have a chance at selecting it. */ - rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); - for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); - list; list = (rtl ? list->prev : list->next)) - { - PsppSheetViewColumn *candidate = list->data; - - if (!candidate->visible) - continue; - - background_area.width = candidate->width; - if ((background_area.x > (gint) event->x) || - (background_area.x + background_area.width <= (gint) event->x)) - { - background_area.x += background_area.width; - continue; - } - - /* we found the focus column */ - column = candidate; - cell_area = background_area; - cell_area.width -= horizontal_separator; - cell_area.height -= vertical_separator; - cell_area.x += horizontal_separator/2; - cell_area.y += vertical_separator/2; - break; - } - - if (column == NULL) - { - gtk_tree_path_free (path); - grab_focus_and_unset_draw_keyfocus (tree_view); - return FALSE; - } - - tree_view->priv->focus_column = column; - - /* decide if we edit */ - if (event->type == GDK_BUTTON_PRESS && event->button == 1 && - !(event->state & gtk_accelerator_get_default_mod_mask ())) - { - GtkTreePath *anchor; - GtkTreeIter iter; - - gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - pspp_sheet_view_column_cell_set_cell_data (column, - tree_view->priv->model, - &iter); - - if (tree_view->priv->anchor) - anchor = gtk_tree_row_reference_get_path (tree_view->priv->anchor); - else - anchor = NULL; - - if ((anchor && !gtk_tree_path_compare (anchor, path)) - || !_pspp_sheet_view_column_has_editable_cell (column)) - { - GtkCellEditable *cell_editable = NULL; + } - /* FIXME: get the right flags */ - guint flags = 0; + if (!find_click (tree_view, event->x, event->y, &node, &column, + &background_area, &cell_area)) + { + grab_focus_and_unset_draw_keyfocus (tree_view); + return FALSE; + } - path_string = gtk_tree_path_to_string (path); + tree_view->priv->focus_column = column; - if (_pspp_sheet_view_column_cell_event (column, - &cell_editable, - (GdkEvent *)event, - path_string, - &background_area, - &cell_area, flags)) - { - if (cell_editable != NULL) - { - gint left, right; - GdkRectangle area; - - area = cell_area; - _pspp_sheet_view_column_get_neighbor_sizes (column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right); - - area.x += left; - area.width -= right + left; - - pspp_sheet_view_real_start_editing (tree_view, - column, - path, - cell_editable, - &area, - (GdkEvent *)event, - flags); - g_free (path_string); - gtk_tree_path_free (path); - gtk_tree_path_free (anchor); - return TRUE; - } - column_handled_click = TRUE; - } - g_free (path_string); - } - if (anchor) - gtk_tree_path_free (anchor); - } + if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event)) + return TRUE; /* select */ - node_selected = pspp_sheet_view_node_is_selected (tree_view, node); pre_val = tree_view->priv->vadjustment->value; + path = _pspp_sheet_view_find_path (tree_view, node); + /* we only handle selection modifications on the first button press */ if (event->type == GDK_BUTTON_PRESS) @@ -2230,7 +2376,7 @@ pspp_sheet_view_button_press (GtkWidget *widget, } else if (event->state & GDK_SHIFT_MASK) { - pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE); + pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE); pspp_sheet_view_real_select_cursor_row (tree_view, FALSE); } else @@ -2238,6 +2384,14 @@ pspp_sheet_view_button_press (GtkWidget *widget, pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE); } + if (tree_view->priv->anchor_column == NULL || + !(event->state & GDK_SHIFT_MASK)) + tree_view->priv->anchor_column = column; + pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection); + pspp_sheet_selection_select_column_range (tree_view->priv->selection, + tree_view->priv->anchor_column, + column); + tree_view->priv->ctrl_pressed = FALSE; tree_view->priv->shift_pressed = FALSE; } @@ -2254,17 +2408,17 @@ pspp_sheet_view_button_press (GtkWidget *widget, /* Save press to possibly begin a drag */ - if (!column_handled_click && - !tree_view->priv->in_grab && + if (!tree_view->priv->in_grab && tree_view->priv->pressed_button < 0) { tree_view->priv->pressed_button = event->button; tree_view->priv->press_start_x = event->x; tree_view->priv->press_start_y = event->y; + tree_view->priv->press_start_node = node; if (tree_view->priv->rubber_banding_enable - && !node_selected - && tree_view->priv->selection->type == GTK_SELECTION_MULTIPLE) + && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE || + tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)) { tree_view->priv->press_start_y += tree_view->priv->dy; tree_view->priv->rubber_band_x = event->x; @@ -2275,6 +2429,7 @@ pspp_sheet_view_button_press (GtkWidget *widget, tree_view->priv->rubber_band_ctrl = TRUE; if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) tree_view->priv->rubber_band_shift = TRUE; + } } @@ -2354,7 +2509,7 @@ pspp_sheet_view_button_press (GtkWidget *widget, gtk_grab_add (widget); PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE); - column->resized_width = column->width - tree_view->priv->last_extra_space_per_column; + column->resized_width = column->width; /* block attached dnd signal handler */ drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); @@ -2365,7 +2520,7 @@ pspp_sheet_view_button_press (GtkWidget *widget, drag_data); tree_view->priv->drag_pos = i; - tree_view->priv->x_drag = column->button->allocation.x + (rtl ? 0 : column->button->allocation.width); + tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width); if (!gtk_widget_has_focus (widget)) gtk_widget_grab_focus (widget); @@ -2392,6 +2547,8 @@ pspp_sheet_view_button_release_drag_column (GtkWidget *widget, gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME); /* Move the button back */ + g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE); + g_object_ref (tree_view->priv->drag_column->button); gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button); gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window); @@ -2468,12 +2625,103 @@ pspp_sheet_view_button_release_column_resize (GtkWidget *widget, return TRUE; } +static gboolean +pspp_sheet_view_button_release_edit (PsppSheetView *tree_view, + GdkEventButton *event) +{ + GtkCellEditable *cell_editable; + gchar *path_string; + GtkTreePath *path; + gint left, right; + GtkTreeIter iter; + PsppSheetViewColumn *column; + GdkRectangle background_area; + GdkRectangle cell_area; + GdkRectangle area; + guint modifiers; + guint flags; + int node; + + if (event->window != tree_view->priv->bin_window) + return FALSE; + + /* Ignore a released button, if that button wasn't depressed */ + if (tree_view->priv->pressed_button != event->button) + return FALSE; + + if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area, + &cell_area)) + return FALSE; + + /* decide if we edit */ + path = _pspp_sheet_view_find_path (tree_view, node); + modifiers = event->state & gtk_accelerator_get_default_mod_mask (); + if (event->button != 1 || modifiers) + return FALSE; + + gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); + pspp_sheet_view_column_cell_set_cell_data (column, + tree_view->priv->model, + &iter); + + if (!pspp_sheet_view_column_get_quick_edit (column) + && _pspp_sheet_view_column_has_editable_cell (column)) + return FALSE; + + flags = 0; /* FIXME: get the right flags */ + path_string = gtk_tree_path_to_string (path); + + if (!_pspp_sheet_view_column_cell_event (column, + &cell_editable, + (GdkEvent *)event, + path_string, + &background_area, + &cell_area, flags)) + return FALSE; + + if (cell_editable == NULL) + return FALSE; + + pspp_sheet_view_real_set_cursor (tree_view, path, + TRUE, TRUE); + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + + area = cell_area; + _pspp_sheet_view_column_get_neighbor_sizes ( + column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right); + + area.x += left; + area.width -= right + left; + + pspp_sheet_view_real_start_editing (tree_view, + column, + path, + cell_editable, + &area, + (GdkEvent *)event, + flags); + g_free (path_string); + gtk_tree_path_free (path); + return TRUE; +} + static gboolean pspp_sheet_view_button_release (GtkWidget *widget, GdkEventButton *event) { PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); + pspp_sheet_view_stop_editing (tree_view, FALSE); + if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE + && pspp_sheet_view_button_release_edit (tree_view, event)) + { + if (tree_view->priv->pressed_button == event->button) + tree_view->priv->pressed_button = -1; + + tree_view->priv->rubber_band_status = RUBBER_BAND_OFF; + return TRUE; + } + if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG)) return pspp_sheet_view_button_release_drag_column (widget, event); @@ -2535,10 +2783,10 @@ prelight_or_select (PsppSheetView *tree_view, gint x, gint y) { - GtkSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection); + PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection); if (tree_view->priv->hover_selection && - (mode == GTK_SELECTION_SINGLE || mode == GTK_SELECTION_BROWSE) && + (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) && !(tree_view->priv->edited_column && tree_view->priv->edited_column->editable_widget)) { @@ -2559,7 +2807,7 @@ prelight_or_select (PsppSheetView *tree_view, } } - else if (mode == GTK_SELECTION_SINGLE) + else if (mode == PSPP_SHEET_SELECTION_SINGLE) pspp_sheet_selection_unselect_all (tree_view->priv->selection); } @@ -2652,9 +2900,9 @@ pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view) GdkRectangle visible_rect; pspp_sheet_view_get_visible_rect (tree_view, &visible_rect); if (reorder->left_column) - x = reorder->left_column->button->allocation.x + reorder->left_column->button->allocation.width; + x = reorder->left_column->allocation.x + reorder->left_column->allocation.width; else - x = reorder->right_column->button->allocation.x; + x = reorder->right_column->allocation.x; if (x < visible_rect.x) arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT; @@ -2683,8 +2931,8 @@ pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view) attributes.wclass = GDK_INPUT_OUTPUT; attributes.x = tree_view->priv->drag_column_x; attributes.y = 0; - width = attributes.width = tree_view->priv->drag_column->button->allocation.width; - height = attributes.height = tree_view->priv->drag_column->button->allocation.height; + width = attributes.width = tree_view->priv->drag_column->allocation.width; + height = attributes.height = tree_view->priv->drag_column->allocation.height; attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view)); attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view)); attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK; @@ -2720,13 +2968,13 @@ pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view) gdk_window_get_origin (tree_view->priv->header_window, &x, &y); if (reorder->left_column) { - x += reorder->left_column->button->allocation.x + reorder->left_column->button->allocation.width - width/2; - height = reorder->left_column->button->allocation.height; + x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2; + height = reorder->left_column->allocation.height; } else { - x += reorder->right_column->button->allocation.x - width/2; - height = reorder->right_column->button->allocation.height; + x += reorder->right_column->allocation.x - width/2; + height = reorder->right_column->allocation.height; } y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */ height += tree_view->priv->expander_size; @@ -2799,9 +3047,9 @@ pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view) x += widget->allocation.width - width; if (reorder->left_column) - height = reorder->left_column->button->allocation.height; + height = reorder->left_column->allocation.height; else - height = reorder->right_column->button->allocation.height; + height = reorder->right_column->allocation.height; y -= tree_view->priv->expander_size; height += 2*tree_view->priv->expander_size; @@ -2898,8 +3146,10 @@ pspp_sheet_view_motion_resize_column (GtkWidget *widget, { column->use_resized_width = TRUE; column->resized_width = new_width; +#if 0 if (column->expand) column->resized_width -= tree_view->priv->last_extra_space_per_column; +#endif gtk_widget_queue_resize (widget); } @@ -3003,7 +3253,7 @@ pspp_sheet_view_motion_drag_column (GtkWidget *widget, /* Handle moving the header */ gdk_window_get_position (tree_view->priv->drag_window, &x, &y); x = CLAMP (x + (gint)event->x - column->drag_x, 0, - MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width) - column->button->allocation.width); + MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width) - column->allocation.width); gdk_window_move (tree_view->priv->drag_window, x, y); /* autoscroll, if needed */ @@ -3251,6 +3501,7 @@ pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view) GdkRectangle new_area; GdkRectangle common; GdkRegion *invalid_region; + PsppSheetViewColumn *column; old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x); old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy; @@ -3293,6 +3544,14 @@ pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view) tree_view->priv->rubber_band_x = x; tree_view->priv->rubber_band_y = y; + pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL); + + pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection); + pspp_sheet_selection_select_column_range (tree_view->priv->selection, + tree_view->priv->anchor_column, + column); + + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); pspp_sheet_view_update_rubber_band_selection (tree_view); } @@ -3305,6 +3564,7 @@ pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view, GdkRectangle rect; GdkRectangle rubber_rect; + return; rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x); rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy; rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1; @@ -3354,6 +3614,15 @@ pspp_sheet_view_motion_bin_window (GtkWidget *widget, if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START) { + GdkRectangle background_area, cell_area; + PsppSheetViewColumn *column; + + if (find_click (tree_view, event->x, event->y, &node, &column, + &background_area, &cell_area) + && tree_view->priv->focus_column == column + && tree_view->priv->press_start_node == node) + return FALSE; + gtk_grab_add (GTK_WIDGET (tree_view)); pspp_sheet_view_update_rubber_band (tree_view); @@ -3455,7 +3724,9 @@ draw_empty_focus (PsppSheetView *tree_view, GdkRectangle *clip_area) static void pspp_sheet_view_draw_grid_lines (PsppSheetView *tree_view, GdkEventExpose *event, - gint n_visible_columns) + gint n_visible_columns, + gint min_y, + gint max_y) { GList *list = tree_view->priv->columns; gint i = 0; @@ -3482,10 +3753,12 @@ pspp_sheet_view_draw_grid_lines (PsppSheetView *tree_view, current_x += column->width; - gdk_draw_line (event->window, - tree_view->priv->grid_line_gc, - current_x - 1, 0, - current_x - 1, height); + if (current_x - 1 >= event->area.x + && current_x - 1 < event->area.x + event->area.width) + gdk_draw_line (event->window, + tree_view->priv->grid_line_gc[GTK_WIDGET(tree_view)->state], + current_x - 1, min_y, + current_x - 1, max_y - min_y); } } @@ -3509,7 +3782,6 @@ pspp_sheet_view_bin_expose (GtkWidget *widget, gint new_y; gint y_offset, cell_offset; gint max_height; - gint depth; GdkRectangle background_area; GdkRectangle cell_area; guint flags; @@ -3528,6 +3800,7 @@ pspp_sheet_view_bin_expose (GtkWidget *widget, gint grid_line_width; gboolean row_ending_details; gboolean draw_vgrid_lines, draw_hgrid_lines; + gint min_y, max_y; rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); @@ -3581,7 +3854,6 @@ pspp_sheet_view_bin_expose (GtkWidget *widget, gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - depth = gtk_tree_path_get_depth (path); gtk_tree_path_free (path); cursor_path = NULL; @@ -3635,12 +3907,14 @@ pspp_sheet_view_bin_expose (GtkWidget *widget, * order, drawing each successive node. */ + min_y = y_offset; do { gboolean parity; gboolean is_first = FALSE; gboolean is_last = FALSE; gboolean done = FALSE; + gboolean selected; max_height = ROW_HEIGHT (tree_view); @@ -3648,32 +3922,37 @@ pspp_sheet_view_bin_expose (GtkWidget *widget, background_area.y = y_offset + event->area.y; background_area.height = max_height; + max_y = background_area.y + max_height; flags = 0; if (node == tree_view->priv->prelight_node) flags |= GTK_CELL_RENDERER_PRELIT; - if (pspp_sheet_view_node_is_selected (tree_view, node)) - flags |= GTK_CELL_RENDERER_SELECTED; + selected = pspp_sheet_view_node_is_selected (tree_view, node); parity = node % 2; - /* we *need* to set cell data on all cells before the call - * to _has_special_cell, else _has_special_cell() does not - * return a correct value. - */ - for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); - list; - list = (rtl ? list->prev : list->next)) + if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT) { - PsppSheetViewColumn *column = list->data; - pspp_sheet_view_column_cell_set_cell_data (column, - tree_view->priv->model, - &iter); - } + /* we *need* to set cell data on all cells before the call + * to _has_special_cell, else _has_special_cell() does not + * return a correct value. + */ + for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); + list; + list = (rtl ? list->prev : list->next)) + { + PsppSheetViewColumn *column = list->data; + pspp_sheet_view_column_cell_set_cell_data (column, + tree_view->priv->model, + &iter); + } - has_special_cell = pspp_sheet_view_has_special_cell (tree_view); + has_special_cell = pspp_sheet_view_has_special_cell (tree_view); + } + else + has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES; for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); list; @@ -3681,11 +3960,17 @@ pspp_sheet_view_bin_expose (GtkWidget *widget, { PsppSheetViewColumn *column = list->data; const gchar *detail = NULL; + gboolean selected_column; GtkStateType state; if (!column->visible) continue; + if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) + selected_column = column->selected && column->selectable; + else + selected_column = TRUE; + if (cell_offset > event->area.x + event->area.width || cell_offset + column->width < event->area.x) { @@ -3693,6 +3978,11 @@ pspp_sheet_view_bin_expose (GtkWidget *widget, continue; } + if (selected && selected_column) + flags |= GTK_CELL_RENDERER_SELECTED; + else + flags &= ~GTK_CELL_RENDERER_SELECTED; + if (column->show_sort_indicator) flags |= GTK_CELL_RENDERER_SORTED; else @@ -3847,14 +4137,14 @@ pspp_sheet_view_bin_expose (GtkWidget *widget, { if (background_area.y > 0) gdk_draw_line (event->window, - tree_view->priv->grid_line_gc, + tree_view->priv->grid_line_gc[widget->state], background_area.x, background_area.y, background_area.x + background_area.width, background_area.y); if (y_offset + max_height >= event->area.height) gdk_draw_line (event->window, - tree_view->priv->grid_line_gc, + tree_view->priv->grid_line_gc[widget->state], background_area.x, background_area.y + max_height, background_area.x + background_area.width, background_area.y + max_height); @@ -3884,6 +4174,21 @@ pspp_sheet_view_bin_expose (GtkWidget *widget, cell_offset += column->width; } + if (cell_offset < event->area.x) + { + gtk_paint_flat_box (widget->style, + event->window, + GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + &event->area, + widget, + "base", + cell_offset, + background_area.y, + event->area.x - cell_offset, + background_area.height); + } + if (node == drag_highlight) { /* Draw indicator for the drop @@ -4023,7 +4328,8 @@ pspp_sheet_view_bin_expose (GtkWidget *widget, while (y_offset < event->area.height); done: - pspp_sheet_view_draw_grid_lines (tree_view, event, n_visible_columns); + pspp_sheet_view_draw_grid_lines (tree_view, event, n_visible_columns, + min_y, max_y); if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE) { @@ -4051,7 +4357,7 @@ done: static gboolean pspp_sheet_view_expose (GtkWidget *widget, - GdkEventExpose *event) + GdkEventExpose *event) { PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget); @@ -4080,20 +4386,47 @@ pspp_sheet_view_expose (GtkWidget *widget, else if (event->window == tree_view->priv->header_window) { + gint n_visible_columns; GList *list; - + + gtk_paint_flat_box (widget->style, + event->window, + GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + &event->area, + widget, + "cell_odd", + event->area.x, + event->area.y, + event->area.width, + event->area.height); + for (list = tree_view->priv->columns; list != NULL; list = list->next) { PsppSheetViewColumn *column = list->data; - if (column == tree_view->priv->drag_column) + if (column == tree_view->priv->drag_column || !column->visible) continue; - if (column->visible) - gtk_container_propagate_expose (GTK_CONTAINER (tree_view), - column->button, - event); + if (span_intersects (column->allocation.x, column->allocation.width, + event->area.x, event->area.width) + && column->button != NULL) + gtk_container_propagate_expose (GTK_CONTAINER (tree_view), + column->button, event); } + + n_visible_columns = 0; + for (list = tree_view->priv->columns; list; list = list->next) + { + if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible) + continue; + n_visible_columns ++; + } + pspp_sheet_view_draw_grid_lines (tree_view, + event, + n_visible_columns, + event->area.y, + event->area.height); } else if (event->window == tree_view->priv->drag_window) { @@ -4351,7 +4684,7 @@ pspp_sheet_view_key_press (GtkWidget *widget, { PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data); - if (gtk_widget_has_focus (column->button)) + if (column->button && gtk_widget_has_focus (column->button)) break; } @@ -4380,7 +4713,7 @@ pspp_sheet_view_key_press (GtkWidget *widget, column->resized_width = 0; if (column->min_width == -1) - column->resized_width = MAX (column->button->requisition.width, + column->resized_width = MAX (column->button_request, column->resized_width); else column->resized_width = MAX (column->min_width, @@ -4697,7 +5030,6 @@ validate_row (PsppSheetView *tree_view, gint horizontal_separator; gint vertical_separator; gint focus_line_width; - gboolean retval = FALSE; gboolean draw_vgrid_lines, draw_hgrid_lines; gint focus_pad; gint grid_line_width; @@ -4760,10 +5092,7 @@ validate_row (PsppSheetView *tree_view, } if (tmp_width > column->requested_width) - { - retval = TRUE; - column->requested_width = tmp_width; - } + column->requested_width = tmp_width; } if (draw_hgrid_lines) @@ -4988,6 +5317,9 @@ initialize_fixed_height_mode (PsppSheetView *tree_view) if (!tree_view->priv->row_count) return; + if (tree_view->priv->fixed_height_set) + return; + if (tree_view->priv->fixed_height < 0) { GtkTreeIter iter; @@ -5001,6 +5333,8 @@ initialize_fixed_height_mode (PsppSheetView *tree_view) tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path); gtk_tree_path_free (path); + + g_object_notify (G_OBJECT (tree_view), "fixed-height"); } } @@ -5031,6 +5365,9 @@ do_presize_handler (PsppSheetView *tree_view) validate_visible_area (tree_view); tree_view->priv->presize_handler_timer = 0; + if (! gtk_widget_get_realized (GTK_WIDGET (tree_view))) + return FALSE; + gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition); tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width); @@ -5470,6 +5807,7 @@ scroll_row_timeout (gpointer data) { PsppSheetView *tree_view = data; + pspp_sheet_view_horizontal_autoscroll (tree_view); pspp_sheet_view_vertical_autoscroll (tree_view); if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE) @@ -6220,6 +6558,9 @@ pspp_sheet_view_has_special_cell (PsppSheetView *tree_view) { GList *list; + if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT) + return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES; + for (list = tree_view->priv->columns; list; list = list->next) { if (!((PsppSheetViewColumn *)list->data)->visible) @@ -6231,6 +6572,27 @@ pspp_sheet_view_has_special_cell (PsppSheetView *tree_view) return FALSE; } +static void +pspp_sheet_view_focus_column (PsppSheetView *tree_view, + PsppSheetViewColumn *focus_column, + gboolean clamp_column_visible) +{ + g_return_if_fail (focus_column != NULL); + + tree_view->priv->focus_column = focus_column; + if (!focus_column->button) + { + pspp_sheet_view_column_set_need_button (focus_column, TRUE); + g_return_if_fail (focus_column->button != NULL); + } + + if (GTK_CONTAINER (tree_view)->focus_child != focus_column->button) + gtk_widget_grab_focus (focus_column->button); + + if (clamp_column_visible) + pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE); +} + /* Returns TRUE if the focus is within the headers, after the focus operation is * done */ @@ -6240,7 +6602,7 @@ pspp_sheet_view_header_focus (PsppSheetView *tree_view, gboolean clamp_column_visible) { GtkWidget *focus_child; - + PsppSheetViewColumn *focus_column; GList *last_column, *first_column; GList *tmp_list; gboolean rtl; @@ -6253,10 +6615,9 @@ pspp_sheet_view_header_focus (PsppSheetView *tree_view, first_column = tree_view->priv->columns; while (first_column) { - if (gtk_widget_get_can_focus (PSPP_SHEET_VIEW_COLUMN (first_column->data)->button) && - PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible && - (PSPP_SHEET_VIEW_COLUMN (first_column->data)->clickable || - PSPP_SHEET_VIEW_COLUMN (first_column->data)->reorderable)) + PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data); + + if (pspp_sheet_view_column_can_focus (c) && c->visible) break; first_column = first_column->next; } @@ -6269,10 +6630,9 @@ pspp_sheet_view_header_focus (PsppSheetView *tree_view, last_column = g_list_last (tree_view->priv->columns); while (last_column) { - if (gtk_widget_get_can_focus (PSPP_SHEET_VIEW_COLUMN (last_column->data)->button) && - PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible && - (PSPP_SHEET_VIEW_COLUMN (last_column->data)->clickable || - PSPP_SHEET_VIEW_COLUMN (last_column->data)->reorderable)) + PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data); + + if (pspp_sheet_view_column_can_focus (c) && c->visible) break; last_column = last_column->prev; } @@ -6289,12 +6649,13 @@ pspp_sheet_view_header_focus (PsppSheetView *tree_view, if (focus_child == NULL) { if (tree_view->priv->focus_column != NULL && - gtk_widget_get_can_focus (tree_view->priv->focus_column->button)) - focus_child = tree_view->priv->focus_column->button; + pspp_sheet_view_column_can_focus (tree_view->priv->focus_column)) + focus_column = tree_view->priv->focus_column; else - focus_child = PSPP_SHEET_VIEW_COLUMN (first_column->data)->button; - gtk_widget_grab_focus (focus_child); - break; + focus_column = first_column->data; + pspp_sheet_view_focus_column (tree_view, focus_column, + clamp_column_visible); + return TRUE; } return FALSE; @@ -6303,20 +6664,25 @@ pspp_sheet_view_header_focus (PsppSheetView *tree_view, if (focus_child == NULL) { if (tree_view->priv->focus_column != NULL) - focus_child = tree_view->priv->focus_column->button; + focus_column = tree_view->priv->focus_column; else if (dir == GTK_DIR_LEFT) - focus_child = PSPP_SHEET_VIEW_COLUMN (last_column->data)->button; + focus_column = last_column->data; else - focus_child = PSPP_SHEET_VIEW_COLUMN (first_column->data)->button; - gtk_widget_grab_focus (focus_child); - break; + focus_column = first_column->data; + pspp_sheet_view_focus_column (tree_view, focus_column, + clamp_column_visible); + return TRUE; } if (gtk_widget_child_focus (focus_child, dir)) { /* The focus moves inside the button. */ /* This is probably a great example of bad UI */ - break; + if (clamp_column_visible) + pspp_sheet_view_clamp_column_visible (tree_view, + tree_view->priv->focus_column, + FALSE); + return TRUE; } /* We need to move the focus among the row of buttons. */ @@ -6328,7 +6694,7 @@ pspp_sheet_view_header_focus (PsppSheetView *tree_view, || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))) { gtk_widget_error_bell (GTK_WIDGET (tree_view)); - break; + return TRUE; } while (tmp_list) @@ -6348,43 +6714,26 @@ pspp_sheet_view_header_focus (PsppSheetView *tree_view, column = tmp_list->data; if (column->button && column->visible && - gtk_widget_get_can_focus (column->button)) + pspp_sheet_view_column_can_focus (column)) { - focus_child = column->button; - gtk_widget_grab_focus (column->button); - break; + pspp_sheet_view_focus_column (tree_view, column, + clamp_column_visible); + return TRUE; } } - break; + return FALSE; + default: g_assert_not_reached (); break; } - /* if focus child is non-null, we assume it's been set to the current focus child - */ - if (focus_child) - { - for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) - if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child) - { - tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data); - break; - } - - if (clamp_column_visible) - { - pspp_sheet_view_clamp_column_visible (tree_view, - tree_view->priv->focus_column, - FALSE); - } - } - - return (focus_child != NULL); + return FALSE; } /* This function returns in 'path' the first focusable path, if the given path * is already focusable, it's the returned one. + * */ static gboolean search_first_focusable_path (PsppSheetView *tree_view, @@ -6392,6 +6741,8 @@ search_first_focusable_path (PsppSheetView *tree_view, gboolean search_forward, int *new_node) { + /* XXX this function is trivial given that the sheetview doesn't support + separator rows */ int node = -1; if (!path || !*path) @@ -6481,9 +6832,18 @@ pspp_sheet_view_style_set (GtkWidget *widget, if (gtk_widget_get_realized (widget)) { + gint i; + PsppSheetViewPrivate *priv = PSPP_SHEET_VIEW (widget)->priv; + gdk_window_set_back_pixmap (widget->window, NULL, FALSE); gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]); gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL); + for (i = 0; i < 5 ; ++i) + { + g_object_unref (priv->grid_line_gc[i]); + priv->grid_line_gc[i] = gdk_gc_new (widget->window); + gdk_gc_copy (priv->grid_line_gc[i], widget->style->text_aa_gc[i]); + } pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines); } @@ -6501,6 +6861,13 @@ pspp_sheet_view_style_set (GtkWidget *widget, tree_view->priv->fixed_height = -1; + /* Invalidate cached button style. */ + if (tree_view->priv->button_style) + { + g_object_unref (tree_view->priv->button_style); + tree_view->priv->button_style = NULL; + } + gtk_widget_queue_resize (widget); } @@ -6619,8 +6986,9 @@ pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view, switch (step) { - /* currently we make no distinction. When we go bi-di, we need to */ case GTK_MOVEMENT_LOGICAL_POSITIONS: + pspp_sheet_view_move_cursor_tab (tree_view, count); + break; case GTK_MOVEMENT_VISUAL_POSITIONS: pspp_sheet_view_move_cursor_left_right (tree_view, count); break; @@ -6985,8 +7353,8 @@ pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view, if (column == NULL) return; - x = column->button->allocation.x; - width = column->button->allocation.width; + x = column->allocation.x; + width = column->allocation.width; if (width > tree_view->priv->hadjustment->page_size) { @@ -7196,9 +7564,9 @@ pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view, if (tmp_list->next != NULL) { g_assert (tmp_list->next->data); - left = reorder->right_align = (reorder->right_column->button->allocation.x + - reorder->right_column->button->allocation.width + - ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->button->allocation.x)/2; + left = reorder->right_align = (reorder->right_column->allocation.x + + reorder->right_column->allocation.width + + ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2; } else { @@ -7222,6 +7590,7 @@ _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view, g_return_if_fail (tree_view->priv->column_drag_info == NULL); g_return_if_fail (tree_view->priv->cur_reorder == NULL); + g_return_if_fail (column->button); pspp_sheet_view_set_column_drag_info (tree_view, column); @@ -7235,10 +7604,10 @@ _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view, attributes.window_type = GDK_WINDOW_CHILD; attributes.wclass = GDK_INPUT_OUTPUT; - attributes.x = column->button->allocation.x; + attributes.x = column->allocation.x; attributes.y = 0; - attributes.width = column->button->allocation.width; - attributes.height = column->button->allocation.height; + attributes.width = column->allocation.width; + attributes.height = column->allocation.height; attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view)); attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view)); attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK; @@ -7288,8 +7657,8 @@ _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view, gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view)); g_object_unref (column->button); - tree_view->priv->drag_column_x = column->button->allocation.x; - allocation = column->button->allocation; + tree_view->priv->drag_column_x = column->allocation.x; + allocation = column->allocation; allocation.x = 0; gtk_widget_size_allocate (column->button, &allocation); gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window); @@ -7373,8 +7742,8 @@ pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view) if (cursor_path == NULL) { - /* Consult the selection before defaulting to the - * first focusable element + /* There's no cursor. Move the cursor to the first selected row, if any + * are selected, otherwise to the first row in the sheetview. */ GList *selected_rows; GtkTreeModel *model; @@ -7385,6 +7754,7 @@ pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view) if (selected_rows) { + /* XXX we could avoid doing O(n) work to get this result */ cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data)); g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL); g_list_free (selected_rows); @@ -7401,7 +7771,8 @@ pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view) if (cursor_path) { - if (tree_view->priv->selection->type == GTK_SELECTION_MULTIPLE) + if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE || + tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE); else pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE); @@ -7410,6 +7781,7 @@ pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view) if (cursor_path) { + /* Now find a column for the cursor. */ PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS); pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL); @@ -7423,14 +7795,17 @@ pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view) if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible) { tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data); + pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection); + pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column); break; } } + } } } -static void +static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view, gint count) { @@ -7441,24 +7816,24 @@ pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view, gboolean grab_focus = TRUE; if (! gtk_widget_has_focus (GTK_WIDGET (tree_view))) - return; + return FALSE; cursor_path = NULL; if (!gtk_tree_row_reference_valid (tree_view->priv->cursor)) /* FIXME: we lost the cursor; should we get the first? */ - return; + return FALSE; cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node); if (cursor_node < 0) /* FIXME: we lost the cursor; should we get the first? */ - return; + return FALSE; selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection); if (selection_count == 0 - && tree_view->priv->selection->type != GTK_SELECTION_NONE + && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE && !tree_view->priv->ctrl_pressed) { /* Don't move the cursor, but just select the current node */ @@ -7490,7 +7865,8 @@ pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view, * If the list has only one item and multi-selection is set then select * the row (if not yet selected). */ - if (tree_view->priv->selection->type == GTK_SELECTION_MULTIPLE && + if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE || + tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) && new_cursor_node < 0) { if (count == -1) @@ -7544,6 +7920,8 @@ pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view, if (grab_focus) gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + + return new_cursor_node >= 0; } static void @@ -7686,7 +8064,7 @@ pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view, gboolean left, right; column = list->data; - if (column->visible == FALSE) + if (column->visible == FALSE || column->row_head) goto loop_end; pspp_sheet_view_column_cell_set_cell_data (column, @@ -7704,11 +8082,114 @@ pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view, right = list->next ? TRUE : FALSE; } - if (_pspp_sheet_view_column_cell_focus (column, count, left, right)) + if (_pspp_sheet_view_column_cell_focus (column, count, left, right)) + { + tree_view->priv->focus_column = column; + found_column = TRUE; + break; + } + loop_end: + if (count == 1) + list = rtl ? list->prev : list->next; + else + list = rtl ? list->next : list->prev; + } + + if (found_column) + { + _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL); + g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + } + else + { + gtk_widget_error_bell (GTK_WIDGET (tree_view)); + } + + pspp_sheet_view_clamp_column_visible (tree_view, + tree_view->priv->focus_column, TRUE); +} + +static gboolean +try_move_cursor_tab (PsppSheetView *tree_view, + gboolean start_at_focus_column, + gint count) +{ + PsppSheetViewColumn *column; + GtkTreeIter iter; + int cursor_node = -1; + GtkTreePath *cursor_path = NULL; + gboolean rtl; + GList *list; + + if (gtk_tree_row_reference_valid (tree_view->priv->cursor)) + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + else + return TRUE; + + _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node); + if (cursor_node < 0) + return TRUE; + if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE) + { + gtk_tree_path_free (cursor_path); + return TRUE; + } + gtk_tree_path_free (cursor_path); + + rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL; + if (start_at_focus_column) + { + list = (rtl + ? g_list_last (tree_view->priv->columns) + : g_list_first (tree_view->priv->columns)); + if (tree_view->priv->focus_column) + { + for (; list; list = (rtl ? list->prev : list->next)) + { + if (list->data == tree_view->priv->focus_column) + break; + } + } + } + else + { + list = (rtl ^ (count == 1) + ? g_list_first (tree_view->priv->columns) + : g_list_last (tree_view->priv->columns)); + } + + while (list) + { + gboolean left, right; + + column = list->data; + if (column->visible == FALSE || !column->tabbable) + goto loop_end; + + pspp_sheet_view_column_cell_set_cell_data (column, + tree_view->priv->model, + &iter); + + if (rtl) + { + right = list->prev ? TRUE : FALSE; + left = list->next ? TRUE : FALSE; + } + else + { + left = list->prev ? TRUE : FALSE; + right = list->next ? TRUE : FALSE; + } + + if (column->tabbable + && _pspp_sheet_view_column_cell_focus (column, count, left, right)) { tree_view->priv->focus_column = column; - found_column = TRUE; - break; + _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL); + g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + return TRUE; } loop_end: if (count == 1) @@ -7717,22 +8198,30 @@ pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view, list = rtl ? list->next : list->prev; } - if (found_column) - { - if (!pspp_sheet_view_has_special_cell (tree_view)) - _pspp_sheet_view_queue_draw_node (tree_view, - cursor_node, - NULL); - g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - } - else + return FALSE; +} + +static void +pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view, + gint count) +{ + if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) + return; + + if (!try_move_cursor_tab (tree_view, TRUE, count)) { - gtk_widget_error_bell (GTK_WIDGET (tree_view)); + /* Shift+Tab goes backward, but Shift isn't supposed to act as Shift does + for other movement commands, that is, it shouldn't cause the selection + to be extended, so we need to act as though it is off. */ + tree_view->priv->shift_pressed = FALSE; + + if (pspp_sheet_view_move_cursor_up_down (tree_view, count) + && !try_move_cursor_tab (tree_view, FALSE, count)) + gtk_widget_error_bell (GTK_WIDGET (tree_view)); } pspp_sheet_view_clamp_column_visible (tree_view, - tree_view->priv->focus_column, TRUE); + tree_view->priv->focus_column, TRUE); } static void @@ -7789,7 +8278,8 @@ pspp_sheet_view_real_select_all (PsppSheetView *tree_view) if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) return FALSE; - if (tree_view->priv->selection->type != GTK_SELECTION_MULTIPLE) + if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE && + tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE) return FALSE; pspp_sheet_selection_select_all (tree_view->priv->selection); @@ -7803,7 +8293,8 @@ pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view) if (!gtk_widget_has_focus (GTK_WIDGET (tree_view))) return FALSE; - if (tree_view->priv->selection->type != GTK_SELECTION_MULTIPLE) + if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE && + tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE) return FALSE; pspp_sheet_selection_unselect_all (tree_view->priv->selection); @@ -8074,7 +8565,7 @@ pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view, if (! column->visible) continue; - if (gtk_widget_has_focus (column->button)) + if (column->button && gtk_widget_has_focus (column->button)) { found_focus = TRUE; break; @@ -8150,19 +8641,17 @@ pspp_sheet_view_new_column_width (PsppSheetView *tree_view, */ rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL); column = g_list_nth (tree_view->priv->columns, i)->data; - width = rtl ? (column->button->allocation.x + column->button->allocation.width - *x) : (*x - column->button->allocation.x); + width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x); /* Clamp down the value */ if (column->min_width == -1) - width = MAX (column->button->requisition.width, - width); + width = MAX (column->button_request, width); else - width = MAX (column->min_width, - width); + width = MAX (column->min_width, width); if (column->max_width != -1) width = MIN (width, column->max_width); - *x = rtl ? (column->button->allocation.x + column->button->allocation.width - width) : (column->button->allocation.x + width); + *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width); return width; } @@ -8239,6 +8728,9 @@ adjust_allocation (GtkWidget *widget, adjust_allocation_recurse (widget, &scroll_data); } +void +pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column); + /* Callbacks */ static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment, @@ -8246,6 +8738,7 @@ pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment, { if (gtk_widget_get_realized (GTK_WIDGET (tree_view))) { + GList *list; gint dy; gdk_window_move (tree_view->priv->bin_window, @@ -8293,8 +8786,20 @@ pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment, pspp_sheet_view_dy_to_top_row (tree_view); } - gdk_window_process_updates (tree_view->priv->header_window, TRUE); - gdk_window_process_updates (tree_view->priv->bin_window, TRUE); + for (list = tree_view->priv->columns; list; list = list->next) + { + PsppSheetViewColumn *column = list->data; + GtkAllocation *allocation = &column->allocation; + + if (span_intersects (allocation->x, allocation->width, + tree_view->priv->hadjustment->value, + GTK_WIDGET (tree_view)->allocation.width)) + { + pspp_sheet_view_column_set_need_button (column, TRUE); + if (!column->button) + pspp_sheet_view_column_update_button (column); + } + } } } @@ -8429,7 +8934,6 @@ pspp_sheet_view_set_model (PsppSheetView *tree_view, if (tree_view->priv->model) { gint i; - GtkTreeModelFlags flags; if (tree_view->priv->search_column == -1) { @@ -8463,8 +8967,6 @@ pspp_sheet_view_set_model (PsppSheetView *tree_view, G_CALLBACK (pspp_sheet_view_rows_reordered), tree_view); - flags = gtk_tree_model_get_flags (tree_view->priv->model); - tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL); /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */ @@ -8638,7 +9140,8 @@ pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view, for (list = tree_view->priv->columns; list; list = list->next) { column = list->data; - gtk_widget_unmap (column->button); + if (column->button) + gtk_widget_unmap (column->button); } gdk_window_hide (tree_view->priv->header_window); } @@ -8852,7 +9355,8 @@ pspp_sheet_view_remove_column (PsppSheetView *tree_view, } if (tree_view->priv->n_columns == 0 && - pspp_sheet_view_get_headers_visible (tree_view)) + pspp_sheet_view_get_headers_visible (tree_view) && + tree_view->priv->header_window) gdk_window_hide (tree_view->priv->header_window); gtk_widget_queue_resize (GTK_WIDGET (tree_view)); @@ -9389,6 +9893,11 @@ pspp_sheet_view_set_reorderable (PsppSheetView *tree_view, g_object_notify (G_OBJECT (tree_view), "reorderable"); } +/* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift + is pressed, other rows will be unselected. + + If CLAMP_NODE is true, then the sheetview will scroll to make the row + visible. */ static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view, GtkTreePath *path, @@ -9583,6 +10092,10 @@ pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view, pspp_sheet_view_column_focus_cell (focus_column, focus_cell); if (start_editing) pspp_sheet_view_start_editing (tree_view, path); + + pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection); + pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column); + } } @@ -9736,6 +10249,121 @@ pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view, return TRUE; } +/* Computes 'cell_area' from 'background_area', which must be the background + area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area + as passed to a GtkCellRenderer's "render" function, or to FALSE to compute + the cell area as passed to _pspp_sheet_view_column_cell_render(). + + 'column' is required to properly adjust 'cell_area->x' and + 'cell_area->width'. It may be set to NULL if these values are not of + interest. In this case 'cell_area->x' and 'cell_area->width' will be + returned as 0. */ +static void +pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view, + PsppSheetViewColumn *column, + const GdkRectangle *background_area, + gboolean subtract_focus_rect, + GdkRectangle *cell_area) +{ + gint vertical_separator; + gint horizontal_separator; + + *cell_area = *background_area; + + gtk_widget_style_get (GTK_WIDGET (tree_view), + "vertical-separator", &vertical_separator, + "horizontal-separator", &horizontal_separator, + NULL); + cell_area->x += horizontal_separator / 2; + cell_area->y += vertical_separator / 2; + cell_area->width -= horizontal_separator; + cell_area->height -= vertical_separator; + + if (subtract_focus_rect) + { + int focus_line_width; + + gtk_widget_style_get (GTK_WIDGET (tree_view), + "focus-line-width", &focus_line_width, + NULL); + cell_area->x += focus_line_width; + cell_area->y += focus_line_width; + cell_area->width -= 2 * focus_line_width; + cell_area->height -= 2 * focus_line_width; + } + + if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE) + { + gint grid_line_width; + gtk_widget_style_get (GTK_WIDGET (tree_view), + "grid-line-width", &grid_line_width, + NULL); + + if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL + || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH) + && column != NULL) + { + PsppSheetViewColumn *first_column, *last_column; + GList *list; + + /* Find the last visible column. */ + last_column = NULL; + for (list = g_list_last (tree_view->priv->columns); + list; + list = list->prev) + { + PsppSheetViewColumn *c = list->data; + if (c->visible) + { + last_column = c; + break; + } + } + + /* Find the first visible column. */ + first_column = NULL; + for (list = g_list_first (tree_view->priv->columns); + list; + list = list->next) + { + PsppSheetViewColumn *c = list->data; + if (c->visible) + { + first_column = c; + break; + } + } + + if (column == first_column) + { + cell_area->width -= grid_line_width / 2; + } + else if (column == last_column) + { + cell_area->x += grid_line_width / 2; + cell_area->width -= grid_line_width / 2; + } + else + { + cell_area->x += grid_line_width / 2; + cell_area->width -= grid_line_width; + } + } + + if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL + || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH) + { + cell_area->y += grid_line_width / 2; + cell_area->height -= grid_line_width; + } + } + + if (column == NULL) + { + cell_area->x = 0; + cell_area->width = 0; + } +} /** * pspp_sheet_view_get_cell_area: @@ -9760,9 +10388,7 @@ pspp_sheet_view_get_cell_area (PsppSheetView *tree_view, PsppSheetViewColumn *column, GdkRectangle *rect) { - int node = -1; - gint vertical_separator; - gint horizontal_separator; + GdkRectangle background_area; g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column)); @@ -9770,33 +10396,10 @@ pspp_sheet_view_get_cell_area (PsppSheetView *tree_view, g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view); g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view))); - gtk_widget_style_get (GTK_WIDGET (tree_view), - "vertical-separator", &vertical_separator, - "horizontal-separator", &horizontal_separator, - NULL); - - rect->x = 0; - rect->y = 0; - rect->width = 0; - rect->height = 0; - - if (column) - { - rect->x = column->button->allocation.x + horizontal_separator/2; - rect->width = column->button->allocation.width - horizontal_separator; - } - - if (path) - { - _pspp_sheet_view_find_node (tree_view, path, &node); - - /* Get vertical coords */ - if (node < 0) - return; - - rect->y = CELL_FIRST_PIXEL (tree_view, node, vertical_separator); - rect->height = MAX (CELL_HEIGHT (tree_view, vertical_separator), tree_view->priv->expander_size - vertical_separator); - } + pspp_sheet_view_get_background_area (tree_view, path, column, + &background_area); + pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area, + FALSE, rect); } /** @@ -10568,7 +11171,6 @@ pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view, GdkRectangle background_area; GdkRectangle expose_area; GtkWidget *widget; - gint depth; /* start drawing inside the black outline */ gint x = 1, y = 1; GdkDrawable *drawable; @@ -10583,8 +11185,6 @@ pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view, if (!gtk_widget_get_realized (widget)) return NULL; - depth = gtk_tree_path_get_depth (path); - _pspp_sheet_view_find_node (tree_view, path, &node); @@ -11496,6 +12096,12 @@ pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable, g_signal_handlers_disconnect_by_func (cell_editable, pspp_sheet_view_remove_widget, tree_view); + g_signal_handlers_disconnect_by_func (cell_editable, + pspp_sheet_view_editable_button_press_event, + tree_view); + g_signal_handlers_disconnect_by_func (cell_editable, + pspp_sheet_view_editable_clicked, + tree_view); gtk_container_remove (GTK_CONTAINER (tree_view), GTK_WIDGET (cell_editable)); @@ -11578,6 +12184,178 @@ pspp_sheet_view_start_editing (PsppSheetView *tree_view, return retval; } +static gboolean +pspp_sheet_view_editable_button_press_event (GtkWidget *widget, + GdkEventButton *event, + PsppSheetView *sheet_view) +{ + gint node; + + node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), + "pspp-sheet-view-node")); + return pspp_sheet_view_row_head_clicked (sheet_view, + node, + sheet_view->priv->edited_column, + event); +} + +static void +pspp_sheet_view_editable_clicked (GtkButton *button, + PsppSheetView *sheet_view) +{ + pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL, + sheet_view); +} + +static gboolean +is_all_selected (GtkWidget *widget) +{ + GtkEntryBuffer *buffer; + gint start_pos, end_pos; + + if (!GTK_IS_ENTRY (widget)) + return FALSE; + + buffer = gtk_entry_get_buffer (GTK_ENTRY (widget)); + return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), + &start_pos, &end_pos) + && start_pos == 0 + && end_pos == gtk_entry_buffer_get_length (buffer)); +} + +static gboolean +is_at_left (GtkWidget *widget) +{ + return (GTK_IS_ENTRY (widget) + && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0); +} + +static gboolean +is_at_right (GtkWidget *widget) +{ + GtkEntryBuffer *buffer; + gint length; + + if (!GTK_IS_ENTRY (widget)) + return FALSE; + + buffer = gtk_entry_get_buffer (GTK_ENTRY (widget)); + length = gtk_entry_buffer_get_length (buffer); + return gtk_editable_get_position (GTK_EDITABLE (widget)) == length; +} + +static gboolean +pspp_sheet_view_event (GtkWidget *widget, + GdkEventKey *event, + PsppSheetView *tree_view) +{ + PsppSheetViewColumn *column; + GtkTreePath *path; + gboolean handled; + gboolean cancel; + guint keyval; + guint state; + gint row; + + /* Intercept only key press events. + It would make sense to use "key-press-event" instead of "event", but + GtkEntry attaches its own signal handler to "key-press-event" that runs + before ours and overrides our desired behavior for GDK_Up and GDK_Down. + */ + if (event->type != GDK_KEY_PRESS) + return FALSE; + + if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK)) + { + /* Pass through most keys that include modifiers. */ + if ((event->keyval == GDK_Tab || event->keyval == GDK_ISO_Left_Tab) + && !(event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))) + { + /* Special case for Shift-Tab. */ + } + else + return FALSE; + } + + keyval = event->keyval; + state = event->state & ~(GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK); + cancel = FALSE; + switch (event->keyval) + { + case GDK_Left: case GDK_KP_Left: + if (!is_all_selected (widget) && !is_at_left (widget)) + return FALSE; + break; + + case GDK_Right: case GDK_KP_Right: + if (!is_all_selected (widget) && !is_at_right (widget)) + return FALSE; + break; + + case GDK_Up: case GDK_KP_Up: + case GDK_Down: case GDK_KP_Down: + break; + + case GDK_Page_Up: case GDK_KP_Page_Up: + case GDK_Page_Down: case GDK_KP_Page_Down: + break; + + case GDK_Escape: + cancel = TRUE; + break; + + case GDK_Return: + keyval = GDK_Down; + break; + + case GDK_Tab: case GDK_KP_Tab: + case GDK_ISO_Left_Tab: + keyval = GDK_Tab; + state |= event->state & GDK_SHIFT_MASK; + break; + + default: + return FALSE; + } + + row = tree_view->priv->edited_row; + column = tree_view->priv->edited_column; + path = gtk_tree_path_new_from_indices (row, -1); + + pspp_sheet_view_stop_editing (tree_view, cancel); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + + pspp_sheet_view_set_cursor (tree_view, path, column, FALSE); + gtk_tree_path_free (path); + + handled = gtk_binding_set_activate (edit_bindings, keyval, state, + GTK_OBJECT (tree_view)); + if (handled) + g_signal_stop_emission_by_name (widget, "event"); + + pspp_sheet_view_get_cursor (tree_view, &path, NULL); + pspp_sheet_view_start_editing (tree_view, path); + gtk_tree_path_free (path); + + return handled; +} + +static void +pspp_sheet_view_override_cell_keypresses (GtkWidget *widget, + gpointer data) +{ + PsppSheetView *sheet_view = data; + + g_signal_connect (widget, "event", + G_CALLBACK (pspp_sheet_view_event), + sheet_view); + + if (GTK_IS_CONTAINER (widget)) + gtk_container_foreach (GTK_CONTAINER (widget), + pspp_sheet_view_override_cell_keypresses, + data); +} + static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view, PsppSheetViewColumn *column, @@ -11587,15 +12365,25 @@ pspp_sheet_view_real_start_editing (PsppSheetView *tree_view, GdkEvent *event, guint flags) { + PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection); gint pre_val = tree_view->priv->vadjustment->value; GtkRequisition requisition; + gint row; + + g_return_if_fail (gtk_tree_path_get_depth (path) == 1); tree_view->priv->edited_column = column; _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable)); + row = gtk_tree_path_get_indices (path)[0]; + tree_view->priv->edited_row = row; pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE); cell_area->y += pre_val - (int)tree_view->priv->vadjustment->value; + pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection); + pspp_sheet_selection_select_column (tree_view->priv->selection, column); + tree_view->priv->anchor_column = column; + gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition); PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS); @@ -11622,11 +12410,26 @@ pspp_sheet_view_real_start_editing (PsppSheetView *tree_view, gtk_widget_grab_focus (GTK_WIDGET (cell_editable)); g_signal_connect (cell_editable, "remove-widget", G_CALLBACK (pspp_sheet_view_remove_widget), tree_view); + if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head && + GTK_IS_BUTTON (cell_editable)) + { + g_signal_connect (cell_editable, "button-press-event", + G_CALLBACK (pspp_sheet_view_editable_button_press_event), + tree_view); + g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node", + GINT_TO_POINTER (row)); + g_signal_connect (cell_editable, "clicked", + G_CALLBACK (pspp_sheet_view_editable_clicked), + tree_view); + } + + pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable), + tree_view); } -static void +void pspp_sheet_view_stop_editing (PsppSheetView *tree_view, - gboolean cancel_editing) + gboolean cancel_editing) { PsppSheetViewColumn *column; GtkCellRenderer *cell; @@ -11667,7 +12470,7 @@ pspp_sheet_view_stop_editing (PsppSheetView *tree_view, * Enables of disables the hover selection mode of @tree_view. * Hover selection makes the selected row follow the pointer. * Currently, this works only for the selection modes - * %GTK_SELECTION_SINGLE and %GTK_SELECTION_BROWSE. + * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE. * * Since: 2.6 **/ @@ -11706,9 +12509,9 @@ pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view) * @tree_view: a #PsppSheetView * @enable: %TRUE to enable rubber banding * - * Enables or disables rubber banding in @tree_view. If the selection mode - * is #GTK_SELECTION_MULTIPLE, rubber banding will allow the user to select - * multiple rows by dragging the mouse. + * Enables or disables rubber banding in @tree_view. If the selection mode is + * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber + * banding will allow the user to select multiple rows by dragging the mouse. * * Since: 2.10 **/ @@ -11731,8 +12534,9 @@ pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view, * @tree_view: a #PsppSheetView * * Returns whether rubber banding is turned on for @tree_view. If the - * selection mode is #GTK_SELECTION_MULTIPLE, rubber banding will allow the - * user to select multiple rows by dragging the mouse. + * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or + * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to + * select multiple rows by dragging the mouse. * * Return value: %TRUE if rubber banding in @tree_view is enabled. * @@ -11834,45 +12638,89 @@ pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view, PsppSheetViewGridLines grid_lines) { PsppSheetViewPrivate *priv; - GtkWidget *widget; PsppSheetViewGridLines old_grid_lines; g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); priv = tree_view->priv; - widget = GTK_WIDGET (tree_view); old_grid_lines = priv->grid_lines; priv->grid_lines = grid_lines; - if (gtk_widget_get_realized (widget)) + if (old_grid_lines != grid_lines) { - if (grid_lines == PSPP_SHEET_VIEW_GRID_LINES_NONE && - priv->grid_line_gc) - { - g_object_unref (priv->grid_line_gc); - priv->grid_line_gc = NULL; - } - - if (grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE && - !priv->grid_line_gc) - { - gint line_width; - - gtk_widget_style_get (widget, - "grid-line-width", &line_width, - NULL); + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - priv->grid_line_gc = gdk_gc_new (widget->window); - gdk_gc_copy (priv->grid_line_gc, widget->style->black_gc); - } + g_object_notify (G_OBJECT (tree_view), "enable-grid-lines"); } +} - if (old_grid_lines != grid_lines) +/** + * pspp_sheet_view_get_special_cells: + * @tree_view: a #PsppSheetView + * + * Returns which grid lines are enabled in @tree_view. + * + * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in + * the sheet view contain special cells. + */ +PsppSheetViewSpecialCells +pspp_sheet_view_get_special_cells (PsppSheetView *tree_view) +{ + g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0); + + return tree_view->priv->special_cells; +} + +/** + * pspp_sheet_view_set_special_cells: + * @tree_view: a #PsppSheetView + * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in + * the sheet view contain special cells. + * + * Sets whether rows in the sheet view contain special cells, controlling the + * rendering of row selections. + */ +void +pspp_sheet_view_set_special_cells (PsppSheetView *tree_view, + PsppSheetViewSpecialCells special_cells) +{ + PsppSheetViewPrivate *priv; + + g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view)); + + priv = tree_view->priv; + + if (priv->special_cells != special_cells) { + priv->special_cells = special_cells; gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - - g_object_notify (G_OBJECT (tree_view), "enable-grid-lines"); + g_object_notify (G_OBJECT (tree_view), "special-cells"); + } +} + +int +pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view) +{ + /* XXX (re)calculate fixed_height if necessary */ + return tree_view->priv->fixed_height; +} + +void +pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view, + int fixed_height) +{ + g_return_if_fail (fixed_height > 0); + + if (tree_view->priv->fixed_height != fixed_height) + { + tree_view->priv->fixed_height = fixed_height; + g_object_notify (G_OBJECT (tree_view), "fixed-height"); + } + if (!tree_view->priv->fixed_height_set) + { + tree_view->priv->fixed_height_set = TRUE; + g_object_notify (G_OBJECT (tree_view), "fixed-height-set"); } } @@ -12206,3 +13054,19 @@ pspp_sheet_view_grid_lines_get_type (void) } return etype; } + +GType +pspp_sheet_view_special_cells_get_type (void) +{ + static GType etype = 0; + if (G_UNLIKELY(etype == 0)) { + static const GEnumValue values[] = { + { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" }, + { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" }, + { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" }, + { 0, NULL, NULL } + }; + etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values); + } + return etype; +}