X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktreeview.c;h=19c5fffa268399ab199c596e4acd2d7e6549648a;hb=5ead07e1b2b959e5309d7f17409c3d4d9b01cf96;hp=df546df2ece6f9c53d5326c2073429b5bbc951e2;hpb=788bec83843059493bd6f3adac26cf8e2f323559;p=~andy%2Fgtk diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index df546df2e..19c5fffa2 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -22,7 +22,6 @@ #include #include -#include "gtkalias.h" #include "gtktreeview.h" #include "gtkrbtree.h" #include "gtktreednd.h" @@ -42,6 +41,8 @@ #include "gtkentry.h" #include "gtkframe.h" #include "gtktreemodelsort.h" +#include "gtkprivate.h" +#include "gtkalias.h" #define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5) #define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2) @@ -176,6 +177,8 @@ static gboolean gtk_tree_view_button_press (GtkWidget *widget, GdkEventButton *event); static gboolean gtk_tree_view_button_release (GtkWidget *widget, GdkEventButton *event); +static gboolean gtk_tree_view_grab_broken (GtkWidget *widget, + GdkEventGrabBroken *event); #if 0 static gboolean gtk_tree_view_configure (GtkWidget *widget, GdkEventConfigure *event); @@ -192,6 +195,8 @@ static void gtk_tree_view_style_set (GtkWidget *widget, GtkStyle *previous_style); static void gtk_tree_view_grab_notify (GtkWidget *widget, gboolean was_grabbed); +static void gtk_tree_view_state_changed (GtkWidget *widget, + GtkStateType previous_state); /* container signals */ static void gtk_tree_view_remove (GtkContainer *container, @@ -282,6 +287,8 @@ static gboolean validate_row (GtkTreeView *tree_view, GtkTreePath *path); static void validate_visible_area (GtkTreeView *tree_view); static gboolean validate_rows_handler (GtkTreeView *tree_view); +static gboolean do_validate_rows (GtkTreeView *tree_view, + gboolean size_request); static gboolean validate_rows (GtkTreeView *tree_view); static gboolean presize_handler_callback (gpointer data); static void install_presize_handler (GtkTreeView *tree_view); @@ -296,6 +303,7 @@ static gboolean gtk_tree_view_is_expander_column (GtkTreeView static void gtk_tree_view_add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask, + gboolean add_shifted_binding, GtkMovementStep step, gint count); static gint gtk_tree_view_unref_and_check_selection_tree (GtkTreeView *tree_view, @@ -378,7 +386,8 @@ static void gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_vi static void gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog, GtkTreeView *tree_view); static void gtk_tree_view_search_position_func (GtkTreeView *tree_view, - GtkWidget *search_dialog); + GtkWidget *search_dialog, + gpointer user_data); static void gtk_tree_view_search_disable_popdown (GtkEntry *entry, GtkMenu *menu, gpointer data); @@ -395,6 +404,9 @@ static gboolean gtk_tree_view_search_delete_event (GtkWidget *widge static gboolean gtk_tree_view_search_button_press_event (GtkWidget *widget, GdkEventButton *event, GtkTreeView *tree_view); +static gboolean gtk_tree_view_search_scroll_event (GtkWidget *entry, + GdkEventScroll *event, + GtkTreeView *tree_view); static gboolean gtk_tree_view_search_key_press_event (GtkWidget *entry, GdkEventKey *event, GtkTreeView *tree_view); @@ -474,7 +486,7 @@ gtk_tree_view_get_type (void) }; tree_view_type = - g_type_register_static (GTK_TYPE_CONTAINER, "GtkTreeView", + g_type_register_static (GTK_TYPE_CONTAINER, I_("GtkTreeView"), &tree_view_info, 0); } @@ -514,6 +526,7 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) widget_class->size_allocate = gtk_tree_view_size_allocate; widget_class->button_press_event = gtk_tree_view_button_press; widget_class->button_release_event = gtk_tree_view_button_release; + widget_class->grab_broken_event = gtk_tree_view_grab_broken; /*widget_class->configure_event = gtk_tree_view_configure;*/ widget_class->motion_notify_event = gtk_tree_view_motion; widget_class->expose_event = gtk_tree_view_expose; @@ -534,6 +547,7 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) widget_class->grab_focus = gtk_tree_view_grab_focus; widget_class->style_set = gtk_tree_view_style_set; widget_class->grab_notify = gtk_tree_view_grab_notify; + widget_class->state_changed = gtk_tree_view_state_changed; /* GtkContainer signals */ container_class->remove = gtk_tree_view_remove; @@ -558,7 +572,7 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) P_("TreeView Model"), P_("The model for the tree view"), GTK_TYPE_TREE_MODEL, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_HADJUSTMENT, @@ -566,7 +580,7 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) P_("Horizontal Adjustment"), P_("Horizontal Adjustment for the widget"), GTK_TYPE_ADJUSTMENT, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_VADJUSTMENT, @@ -574,31 +588,31 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) P_("Vertical Adjustment"), P_("Vertical Adjustment for the widget"), GTK_TYPE_ADJUSTMENT, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_HEADERS_VISIBLE, - g_param_spec_boolean ("headers_visible", - P_("Visible"), + g_param_spec_boolean ("headers-visible", + P_("Headers Visible"), P_("Show the column header buttons"), TRUE, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_HEADERS_CLICKABLE, - g_param_spec_boolean ("headers_clickable", + g_param_spec_boolean ("headers-clickable", P_("Headers Clickable"), P_("Column headers respond to click events"), FALSE, - G_PARAM_WRITABLE)); + GTK_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_EXPANDER_COLUMN, - g_param_spec_object ("expander_column", + g_param_spec_object ("expander-column", P_("Expander Column"), P_("Set the column for the expander column"), GTK_TYPE_TREE_VIEW_COLUMN, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_REORDERABLE, @@ -606,33 +620,33 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) P_("Reorderable"), P_("View is reorderable"), FALSE, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_RULES_HINT, - g_param_spec_boolean ("rules_hint", + g_param_spec_boolean ("rules-hint", P_("Rules Hint"), P_("Set a hint to the theme engine to draw rows in alternating colors"), FALSE, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_ENABLE_SEARCH, - g_param_spec_boolean ("enable_search", + g_param_spec_boolean ("enable-search", P_("Enable Search"), P_("View allows user to search through columns interactively"), TRUE, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); g_object_class_install_property (o_class, PROP_SEARCH_COLUMN, - g_param_spec_int ("search_column", + g_param_spec_int ("search-column", P_("Search Column"), P_("Model column to search through when searching through code"), -1, G_MAXINT, -1, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); /** * GtkTreeView:fixed-height-mode: @@ -647,11 +661,11 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) **/ g_object_class_install_property (o_class, PROP_FIXED_HEIGHT_MODE, - g_param_spec_boolean ("fixed_height_mode", + g_param_spec_boolean ("fixed-height-mode", P_("Fixed Height Mode"), P_("Speeds up GtkTreeView by assuming that all rows have the same height"), FALSE, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); /** * GtkTreeView:hover-selection: @@ -668,11 +682,11 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) */ g_object_class_install_property (o_class, PROP_HOVER_SELECTION, - g_param_spec_boolean ("hover_selection", + g_param_spec_boolean ("hover-selection", P_("Hover Selection"), P_("Whether the selection should follow the pointer"), FALSE, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); /** * GtkTreeView:hover-expand: @@ -688,11 +702,11 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) */ g_object_class_install_property (o_class, PROP_HOVER_EXPAND, - g_param_spec_boolean ("hover_expand", + g_param_spec_boolean ("hover-expand", P_("Hover Expand"), - P_("Whether rows should be expanded/collaped when the pointer moves over them"), + P_("Whether rows should be expanded/collapsed when the pointer moves over them"), FALSE, - G_PARAM_READWRITE)); + GTK_PARAM_READWRITE)); /* Style properties */ #define _TREE_VIEW_EXPANDER_SIZE 12 @@ -700,63 +714,63 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2 gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("expander_size", + g_param_spec_int ("expander-size", P_("Expander Size"), P_("Size of the expander arrow"), 0, G_MAXINT, _TREE_VIEW_EXPANDER_SIZE, - G_PARAM_READABLE)); + GTK_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("vertical_separator", + g_param_spec_int ("vertical-separator", P_("Vertical Separator Width"), P_("Vertical space between cells. Must be an even number"), 0, G_MAXINT, _TREE_VIEW_VERTICAL_SEPARATOR, - G_PARAM_READABLE)); + GTK_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("horizontal_separator", + g_param_spec_int ("horizontal-separator", P_("Horizontal Separator Width"), P_("Horizontal space between cells. Must be an even number"), 0, G_MAXINT, _TREE_VIEW_HORIZONTAL_SEPARATOR, - G_PARAM_READABLE)); + GTK_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, - g_param_spec_boolean ("allow_rules", + g_param_spec_boolean ("allow-rules", P_("Allow Rules"), P_("Allow drawing of alternating color rows"), TRUE, - G_PARAM_READABLE)); + GTK_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, - g_param_spec_boolean ("indent_expanders", + g_param_spec_boolean ("indent-expanders", P_("Indent Expanders"), P_("Make the expanders indented"), TRUE, - G_PARAM_READABLE)); + GTK_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, - g_param_spec_boxed ("even_row_color", + g_param_spec_boxed ("even-row-color", P_("Even Row Color"), P_("Color to use for even rows"), GDK_TYPE_COLOR, -G_PARAM_READABLE)); + GTK_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, - g_param_spec_boxed ("odd_row_color", + g_param_spec_boxed ("odd-row-color", P_("Odd Row Color"), P_("Color to use for odd rows"), GDK_TYPE_COLOR, -G_PARAM_READABLE)); + GTK_PARAM_READABLE)); /* Signals */ widget_class->set_scroll_adjustments_signal = - g_signal_new ("set_scroll_adjustments", + g_signal_new (I_("set_scroll_adjustments"), G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, set_scroll_adjustments), @@ -766,8 +780,23 @@ G_PARAM_READABLE)); GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); + /** + * GtkTreeView::row-activated: + * @tree_view: the object on which the signal is emitted + * @path: the #GtkTreePath for the activated row + * @column: the #GtkTreeViewColumn in which the activation occurred + * + * The "row-activated" signal is emitted when the method + * gtk_tree_view_row_activated() is called or the user double clicks + * a treeview row. It is also emitted when a non-editable row is + * selected and one of the keys: Space, Shift+Space, Return or + * Enter is pressed. + * + * For selection handling refer to the tree + * widget conceptual overview as well as #GtkTreeSelection. + */ tree_view_signals[ROW_ACTIVATED] = - g_signal_new ("row_activated", + g_signal_new (I_("row_activated"), G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, row_activated), @@ -778,7 +807,7 @@ G_PARAM_READABLE)); GTK_TYPE_TREE_VIEW_COLUMN); tree_view_signals[TEST_EXPAND_ROW] = - g_signal_new ("test_expand_row", + g_signal_new (I_("test_expand_row"), G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTreeViewClass, test_expand_row), @@ -789,7 +818,7 @@ G_PARAM_READABLE)); GTK_TYPE_TREE_PATH); tree_view_signals[TEST_COLLAPSE_ROW] = - g_signal_new ("test_collapse_row", + g_signal_new (I_("test_collapse_row"), G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTreeViewClass, test_collapse_row), @@ -800,7 +829,7 @@ G_PARAM_READABLE)); GTK_TYPE_TREE_PATH); tree_view_signals[ROW_EXPANDED] = - g_signal_new ("row_expanded", + g_signal_new (I_("row_expanded"), G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTreeViewClass, row_expanded), @@ -811,7 +840,7 @@ G_PARAM_READABLE)); GTK_TYPE_TREE_PATH); tree_view_signals[ROW_COLLAPSED] = - g_signal_new ("row_collapsed", + g_signal_new (I_("row_collapsed"), G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTreeViewClass, row_collapsed), @@ -822,7 +851,7 @@ G_PARAM_READABLE)); GTK_TYPE_TREE_PATH); tree_view_signals[COLUMNS_CHANGED] = - g_signal_new ("columns_changed", + g_signal_new (I_("columns_changed"), G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTreeViewClass, columns_changed), @@ -831,7 +860,7 @@ G_PARAM_READABLE)); G_TYPE_NONE, 0); tree_view_signals[CURSOR_CHANGED] = - g_signal_new ("cursor_changed", + g_signal_new (I_("cursor_changed"), G_TYPE_FROM_CLASS (o_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkTreeViewClass, cursor_changed), @@ -840,7 +869,7 @@ G_PARAM_READABLE)); G_TYPE_NONE, 0); tree_view_signals[MOVE_CURSOR] = - g_signal_new ("move_cursor", + g_signal_new (I_("move_cursor"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, move_cursor), @@ -851,7 +880,7 @@ G_PARAM_READABLE)); G_TYPE_INT); tree_view_signals[SELECT_ALL] = - g_signal_new ("select_all", + g_signal_new (I_("select_all"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, select_all), @@ -860,7 +889,7 @@ G_PARAM_READABLE)); G_TYPE_BOOLEAN, 0); tree_view_signals[UNSELECT_ALL] = - g_signal_new ("unselect_all", + g_signal_new (I_("unselect_all"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, unselect_all), @@ -869,7 +898,7 @@ G_PARAM_READABLE)); G_TYPE_BOOLEAN, 0); tree_view_signals[SELECT_CURSOR_ROW] = - g_signal_new ("select_cursor_row", + g_signal_new (I_("select_cursor_row"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_row), @@ -879,7 +908,7 @@ G_PARAM_READABLE)); G_TYPE_BOOLEAN); tree_view_signals[TOGGLE_CURSOR_ROW] = - g_signal_new ("toggle_cursor_row", + g_signal_new (I_("toggle_cursor_row"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, toggle_cursor_row), @@ -888,7 +917,7 @@ G_PARAM_READABLE)); G_TYPE_BOOLEAN, 0); tree_view_signals[EXPAND_COLLAPSE_CURSOR_ROW] = - g_signal_new ("expand_collapse_cursor_row", + g_signal_new (I_("expand_collapse_cursor_row"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, expand_collapse_cursor_row), @@ -900,7 +929,7 @@ G_PARAM_READABLE)); G_TYPE_BOOLEAN); tree_view_signals[SELECT_CURSOR_PARENT] = - g_signal_new ("select_cursor_parent", + g_signal_new (I_("select_cursor_parent"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, select_cursor_parent), @@ -909,7 +938,7 @@ G_PARAM_READABLE)); G_TYPE_BOOLEAN, 0); tree_view_signals[START_INTERACTIVE_SEARCH] = - g_signal_new ("start_interactive_search", + g_signal_new (I_("start_interactive_search"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkTreeViewClass, start_interactive_search), @@ -918,40 +947,40 @@ G_PARAM_READABLE)); G_TYPE_BOOLEAN, 0); /* Key bindings */ - gtk_tree_view_add_move_binding (binding_set, GDK_Up, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_Up, 0, TRUE, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_tree_view_add_move_binding (binding_set, GDK_KP_Up, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_KP_Up, 0, TRUE, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_tree_view_add_move_binding (binding_set, GDK_Down, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_Down, 0, TRUE, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_tree_view_add_move_binding (binding_set, GDK_KP_Down, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_KP_Down, 0, TRUE, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_tree_view_add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK, + gtk_tree_view_add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK, FALSE, GTK_MOVEMENT_DISPLAY_LINES, -1); - gtk_tree_view_add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK, + gtk_tree_view_add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK, FALSE, GTK_MOVEMENT_DISPLAY_LINES, 1); - gtk_tree_view_add_move_binding (binding_set, GDK_Home, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_Home, 0, TRUE, GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_tree_view_add_move_binding (binding_set, GDK_KP_Home, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_KP_Home, 0, TRUE, GTK_MOVEMENT_BUFFER_ENDS, -1); - gtk_tree_view_add_move_binding (binding_set, GDK_End, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_End, 0, TRUE, GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_tree_view_add_move_binding (binding_set, GDK_KP_End, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_KP_End, 0, TRUE, GTK_MOVEMENT_BUFFER_ENDS, 1); - gtk_tree_view_add_move_binding (binding_set, GDK_Page_Up, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_Page_Up, 0, TRUE, GTK_MOVEMENT_PAGES, -1); - gtk_tree_view_add_move_binding (binding_set, GDK_KP_Page_Up, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_KP_Page_Up, 0, TRUE, GTK_MOVEMENT_PAGES, -1); - gtk_tree_view_add_move_binding (binding_set, GDK_Page_Down, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_Page_Down, 0, TRUE, GTK_MOVEMENT_PAGES, 1); - gtk_tree_view_add_move_binding (binding_set, GDK_KP_Page_Down, 0, + gtk_tree_view_add_move_binding (binding_set, GDK_KP_Page_Down, 0, TRUE, GTK_MOVEMENT_PAGES, 1); @@ -1119,16 +1148,20 @@ G_PARAM_READABLE)); 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); + + g_type_class_add_private (o_class, sizeof (GtkTreeViewPrivate)); } static void gtk_tree_view_init (GtkTreeView *tree_view) { - tree_view->priv = g_new0 (GtkTreeViewPrivate, 1); + tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, GTK_TYPE_TREE_VIEW, GtkTreeViewPrivate); + GTK_WIDGET_SET_FLAGS (tree_view, GTK_CAN_FOCUS); gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE); @@ -1157,8 +1190,9 @@ gtk_tree_view_init (GtkTreeView *tree_view) tree_view->priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view); tree_view->priv->enable_search = TRUE; tree_view->priv->search_column = -1; - tree_view->priv->search_dialog_position_func = gtk_tree_view_search_position_func; + tree_view->priv->search_position_func = gtk_tree_view_search_position_func; tree_view->priv->search_equal_func = gtk_tree_view_search_equal_func; + tree_view->priv->search_custom_entry_set = FALSE; tree_view->priv->typeselect_flush_timeout = 0; tree_view->priv->init_hadjust_value = TRUE; tree_view->priv->width = 0; @@ -1252,6 +1286,9 @@ gtk_tree_view_get_property (GObject *object, case PROP_HEADERS_VISIBLE: g_value_set_boolean (value, gtk_tree_view_get_headers_visible (tree_view)); break; + case PROP_HEADERS_CLICKABLE: + g_value_set_boolean (value, gtk_tree_view_get_headers_clickable (tree_view)); + break; case PROP_EXPANDER_COLUMN: g_value_set_object (value, tree_view->priv->expander_column); break; @@ -1285,10 +1322,6 @@ gtk_tree_view_get_property (GObject *object, static void gtk_tree_view_finalize (GObject *object) { - GtkTreeView *tree_view = (GtkTreeView *) object; - - g_free (tree_view->priv); - (* G_OBJECT_CLASS (parent_class)->finalize) (object); } @@ -1297,6 +1330,20 @@ gtk_tree_view_finalize (GObject *object) /* GtkObject Methods */ +static void +gtk_tree_view_free_rbtree (GtkTreeView *tree_view) +{ + _gtk_rbtree_free (tree_view->priv->tree); + + tree_view->priv->tree = NULL; + tree_view->priv->button_pressed_node = NULL; + tree_view->priv->button_pressed_tree = NULL; + tree_view->priv->prelight_tree = NULL; + tree_view->priv->prelight_node = NULL; + tree_view->priv->expanded_collapsed_node = NULL; + tree_view->priv->expanded_collapsed_tree = NULL; +} + static void gtk_tree_view_destroy (GtkObject *object) { @@ -1321,8 +1368,8 @@ gtk_tree_view_destroy (GtkObject *object) if (tree_view->priv->tree != NULL) { gtk_tree_view_unref_and_check_selection_tree (tree_view, tree_view->priv->tree); - _gtk_rbtree_free (tree_view->priv->tree); - tree_view->priv->tree = NULL; + + gtk_tree_view_free_rbtree (tree_view); } if (tree_view->priv->selection != NULL) @@ -1388,15 +1435,26 @@ gtk_tree_view_destroy (GtkObject *object) gtk_widget_destroy (tree_view->priv->search_window); tree_view->priv->search_window = NULL; tree_view->priv->search_entry = NULL; + if (tree_view->priv->typeselect_flush_timeout) + { + g_source_remove (tree_view->priv->typeselect_flush_timeout); + tree_view->priv->typeselect_flush_timeout = 0; + } } - if (tree_view->priv->search_destroy) + if (tree_view->priv->search_destroy && tree_view->priv->search_user_data) { (* tree_view->priv->search_destroy) (tree_view->priv->search_user_data); tree_view->priv->search_user_data = NULL; } - if (tree_view->priv->row_separator_destroy) + if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data) + { + (* tree_view->priv->search_position_destroy) (tree_view->priv->search_position_user_data); + tree_view->priv->search_position_user_data = NULL; + } + + if (tree_view->priv->row_separator_destroy && tree_view->priv->row_separator_data) { (* tree_view->priv->row_separator_destroy) (tree_view->priv->row_separator_data); tree_view->priv->row_separator_data = NULL; @@ -1496,7 +1554,6 @@ gtk_tree_view_realize (GtkWidget *widget) { GList *tmp_list; GtkTreeView *tree_view; - GdkGCValues values; GdkWindowAttr attributes; gint attributes_mask; @@ -1558,12 +1615,6 @@ gtk_tree_view_realize (GtkWidget *widget) &attributes, attributes_mask); gdk_window_set_user_data (tree_view->priv->header_window, widget); - - values.foreground = (widget->style->white.pixel==0 ? - widget->style->black:widget->style->white); - values.function = GDK_XOR; - values.subwindow_mode = GDK_INCLUDE_INFERIORS; - /* Add them all up. */ widget->style = gtk_style_attach (widget->style, widget->window); gdk_window_set_background (widget->window, &widget->style->base[widget->state]); @@ -1767,7 +1818,7 @@ gtk_tree_view_size_request (GtkWidget *widget, * sure we have some size. In practice, with a lot of static lists, this * should get a good width. */ - validate_rows (tree_view); + do_validate_rows (tree_view, FALSE); gtk_tree_view_size_request_columns (tree_view); gtk_tree_view_update_size (GTK_TREE_VIEW (widget)); @@ -1889,6 +1940,7 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget) gint extra, extra_per_column; gint full_requested_width = 0; gint number_of_expand_columns = 0; + gboolean column_changed = FALSE; gboolean rtl; tree_view = GTK_TREE_VIEW (widget); @@ -1991,7 +2043,7 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget) width += column->width; if (column->width > old_width) - invalidate_column (tree_view, column); + column_changed = TRUE; gtk_widget_size_allocate (column->button, &allocation); @@ -2001,6 +2053,9 @@ gtk_tree_view_size_allocate_columns (GtkWidget *widget) allocation.y, TREE_VIEW_DRAG_WIDTH, allocation.height); } + + if (column_changed) + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); } @@ -2011,7 +2066,6 @@ gtk_tree_view_size_allocate (GtkWidget *widget, GList *tmp_list; GtkTreeView *tree_view; gboolean width_changed = FALSE; - gboolean dy_changed = FALSE; gint old_width = widget->allocation.width; g_return_if_fail (GTK_IS_TREE_VIEW (widget)); @@ -2079,16 +2133,18 @@ gtk_tree_view_size_allocate (GtkWidget *widget, tree_view->priv->vadjustment->lower = 0; tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->page_size, tree_view->priv->height); - if (tree_view->priv->vadjustment->value + allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view) > tree_view->priv->height) - { - double before = tree_view->priv->vadjustment->value; - gtk_adjustment_set_value (tree_view->priv->vadjustment, - MAX (tree_view->priv->height - tree_view->priv->vadjustment->page_size, 0)); - if (before != tree_view->priv->vadjustment->value) - dy_changed = TRUE; - } - gtk_adjustment_changed (tree_view->priv->vadjustment); + + /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */ + if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size) + gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0); + else if (tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size > tree_view->priv->height) + gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), + tree_view->priv->height - tree_view->priv->vadjustment->page_size); + else if (gtk_tree_row_reference_valid (tree_view->priv->top_row)) + gtk_tree_view_top_row_to_dy (tree_view); + else + gtk_tree_view_dy_to_top_row (tree_view); if (GTK_WIDGET_REALIZED (widget)) { @@ -2133,9 +2189,6 @@ gtk_tree_view_size_allocate (GtkWidget *widget, else gtk_widget_queue_draw (widget); } - - if (dy_changed) - gtk_widget_queue_draw (widget); } } @@ -2169,8 +2222,8 @@ gtk_tree_view_button_press (GtkWidget *widget, rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); gtk_tree_view_stop_editing (tree_view, FALSE); gtk_widget_style_get (widget, - "vertical_separator", &vertical_separator, - "horizontal_separator", &horizontal_separator, + "vertical-separator", &vertical_separator, + "horizontal-separator", &horizontal_separator, NULL); @@ -2248,12 +2301,12 @@ gtk_tree_view_button_press (GtkWidget *widget, for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); list; list = (rtl ? list->prev : list->next)) { - column = list->data; + GtkTreeViewColumn *candidate = list->data; - if (!column->visible) + if (!candidate->visible) continue; - background_area.width = column->width; + background_area.width = candidate->width; if ((background_area.x > (gint) event->x) || (background_area.x + background_area.width <= (gint) event->x)) { @@ -2262,6 +2315,7 @@ gtk_tree_view_button_press (GtkWidget *widget, } /* we found the focus column */ + column = candidate; cell_area = background_area; cell_area.width -= horizontal_separator; cell_area.height -= vertical_separator; @@ -2480,6 +2534,7 @@ gtk_tree_view_button_press (GtkWidget *widget, if (event->type == GDK_2BUTTON_PRESS && gtk_tree_view_column_get_sizing (column) != GTK_TREE_VIEW_COLUMN_AUTOSIZE) { + column->use_resized_width = FALSE; _gtk_tree_view_column_autosize (tree_view, column); return TRUE; } @@ -2494,7 +2549,6 @@ gtk_tree_view_button_press (GtkWidget *widget, gtk_grab_add (widget); GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE); column->resized_width = column->width; - column->use_resized_width = TRUE; /* block attached dnd signal handler */ drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); @@ -2586,15 +2640,12 @@ gtk_tree_view_button_release_column_resize (GtkWidget *widget, { GtkTreeView *tree_view; gpointer drag_data; - gint x; - gint i; tree_view = GTK_TREE_VIEW (widget); - i = tree_view->priv->drag_pos; tree_view->priv->drag_pos = -1; - /* unblock attached dnd signal handler */ + /* unblock attached dnd signal handler */ drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data"); if (drag_data) g_signal_handlers_unblock_matched (widget, @@ -2603,7 +2654,6 @@ gtk_tree_view_button_release_column_resize (GtkWidget *widget, drag_data); GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE); - gtk_widget_get_pointer (widget, &x, NULL); gtk_grab_remove (widget); gdk_display_pointer_ungrab (gdk_drawable_get_display (event->window), event->time); @@ -2664,6 +2714,26 @@ gtk_tree_view_button_release (GtkWidget *widget, return TRUE; } +static gboolean +gtk_tree_view_grab_broken (GtkWidget *widget, + GdkEventGrabBroken *event) +{ + GtkTreeView *tree_view; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + tree_view = GTK_TREE_VIEW (widget); + + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG)) + gtk_tree_view_button_release_drag_column (widget, (GdkEventButton *)event); + + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE)) + gtk_tree_view_button_release_column_resize (widget, (GdkEventButton *)event); + + return TRUE; +} + #if 0 static gboolean gtk_tree_view_configure (GtkWidget *widget, @@ -2672,7 +2742,7 @@ gtk_tree_view_configure (GtkWidget *widget, GtkTreeView *tree_view; tree_view = GTK_TREE_VIEW (widget); - tree_view->priv->search_dialog_position_func (tree_view, tree_view->priv->search_window); + tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window); return FALSE; } @@ -3182,8 +3252,9 @@ gtk_tree_view_motion_resize_column (GtkWidget *widget, new_width = gtk_tree_view_new_column_width (tree_view, tree_view->priv->drag_pos, &x); if (x != tree_view->priv->x_drag && - (new_width != column->fixed_width)); + (new_width != column->fixed_width)) { + column->use_resized_width = TRUE; column->resized_width = new_width; gtk_widget_queue_resize (widget); } @@ -3420,14 +3491,16 @@ gtk_tree_view_bin_expose (GtkWidget *widget, GtkRBTree *drag_highlight_tree = NULL; GtkTreeIter iter; gint new_y; - gint y_offset, x_offset, cell_offset; + gint y_offset, cell_offset; gint max_height; gint depth; GdkRectangle background_area; GdkRectangle cell_area; guint flags; gint highlight_x; + gint expander_cell_width; gint bin_window_width; + gint bin_window_height; GtkTreePath *cursor_path; GtkTreePath *drag_dest_path; GList *last_column; @@ -3438,6 +3511,8 @@ gtk_tree_view_bin_expose (GtkWidget *widget, gboolean has_special_cell; gboolean rtl; gint n_visible_columns; + gint pointer_x, pointer_y; + gboolean got_pointer = FALSE; g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); @@ -3446,9 +3521,9 @@ gtk_tree_view_bin_expose (GtkWidget *widget, tree_view = GTK_TREE_VIEW (widget); gtk_widget_style_get (widget, - "horizontal_separator", &horizontal_separator, - "vertical_separator", &vertical_separator, - "allow_rules", &allow_rules, + "horizontal-separator", &horizontal_separator, + "vertical-separator", &vertical_separator, + "allow-rules", &allow_rules, "focus-line-width", &focus_line_width, NULL); @@ -3469,6 +3544,21 @@ gtk_tree_view_bin_expose (GtkWidget *widget, if (new_y < 0) new_y = 0; y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, new_y, &tree, &node); + gdk_drawable_get_size (tree_view->priv->bin_window, + &bin_window_width, &bin_window_height); + + if (tree_view->priv->height < bin_window_height) + { + gtk_paint_flat_box (widget->style, + event->window, + widget->state, + GTK_SHADOW_NONE, + &event->area, + widget, + "cell_even", + 0, tree_view->priv->height, + bin_window_width, bin_window_height); + } if (node == NULL) return TRUE; @@ -3500,9 +3590,6 @@ gtk_tree_view_bin_expose (GtkWidget *widget, _gtk_tree_view_find_node (tree_view, drag_dest_path, &drag_highlight_tree, &drag_highlight); - gdk_drawable_get_size (tree_view->priv->bin_window, - &bin_window_width, NULL); - n_visible_columns = 0; for (list = tree_view->priv->columns; list; list = list->next) @@ -3539,9 +3626,9 @@ gtk_tree_view_bin_expose (GtkWidget *widget, max_height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node)); - x_offset = -event->area.x; cell_offset = 0; highlight_x = 0; /* should match x coord of first cell */ + expander_cell_width = 0; background_area.y = y_offset + event->area.y; background_area.height = max_height; @@ -3556,6 +3643,22 @@ gtk_tree_view_bin_expose (GtkWidget *widget, parity = _gtk_rbtree_node_find_parity (tree, node); + /* 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)) + { + GtkTreeViewColumn *column = list->data; + gtk_tree_view_column_cell_set_cell_data (column, + tree_view->priv->model, + &iter, + GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT), + node->children?TRUE:FALSE); + } + has_special_cell = gtk_tree_view_has_special_cell (tree_view); for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns)); @@ -3650,7 +3753,9 @@ gtk_tree_view_bin_expose (GtkWidget *widget, g_assert (detail); - if (flags & GTK_CELL_RENDERER_SELECTED) + if (widget->state == GTK_STATE_INSENSITIVE) + state = GTK_STATE_INSENSITIVE; + else if (flags & GTK_CELL_RENDERER_SELECTED) state = GTK_STATE_SELECTED; else state = GTK_STATE_NORMAL; @@ -3680,6 +3785,8 @@ gtk_tree_view_bin_expose (GtkWidget *widget, * level of the tree we're dropping at. */ highlight_x = cell_area.x; + expander_cell_width = cell_area.width; + if (is_separator) gtk_paint_hline (widget->style, event->window, @@ -3699,12 +3806,17 @@ gtk_tree_view_bin_expose (GtkWidget *widget, flags); if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT) { - gint x, y; - gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL); + if (!got_pointer) + { + gdk_window_get_pointer (tree_view->priv->bin_window, + &pointer_x, &pointer_y, NULL); + got_pointer = TRUE; + } + gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget), - tree, + tree, node, - x, y); + pointer_x, pointer_y); } } else @@ -3751,7 +3863,6 @@ gtk_tree_view_bin_expose (GtkWidget *widget, GtkRBTree *tree = NULL; GtkRBNode *node = NULL; gint width; - gint focus_line_width; switch (tree_view->priv->drag_dest_pos) { @@ -3773,7 +3884,6 @@ gtk_tree_view_bin_expose (GtkWidget *widget, break; gdk_drawable_get_size (tree_view->priv->bin_window, &width, NULL); - gtk_widget_style_get (widget, "focus-line-width", &focus_line_width, NULL); gtk_paint_focus (widget->style, tree_view->priv->bin_window, GTK_WIDGET_STATE (widget), @@ -3790,10 +3900,10 @@ gtk_tree_view_bin_expose (GtkWidget *widget, if (highlight_y >= 0) { gdk_draw_line (event->window, - widget->style->black_gc, - highlight_x, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + rtl ? highlight_x + expander_cell_width : highlight_x, highlight_y, - bin_window_width - highlight_x, + rtl ? 0 : bin_window_width, highlight_y); } } @@ -4002,7 +4112,7 @@ gtk_tree_view_get_drop_column (GtkTreeView *tree_view, continue; } - return cur_column; + return left_column; } if (!tree_view->priv->column_drop_func) @@ -4053,7 +4163,7 @@ gtk_tree_view_get_drop_column (GtkTreeView *tree_view, continue; } - return cur_column; + return left_column; } if (!tree_view->priv->column_drop_func) @@ -4184,9 +4294,10 @@ gtk_tree_view_key_press (GtkWidget *widget, return TRUE; } - if (tree_view->priv->columns && (event->state & GDK_SHIFT_MASK) - && (event->keyval == GDK_Left || event->keyval == GDK_KP_Left - || event->keyval == GDK_Right || event->keyval == GDK_KP_Right)) + if (tree_view->priv->columns && + (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) && + (event->keyval == GDK_Left || event->keyval == GDK_KP_Left + || event->keyval == GDK_Right || event->keyval == GDK_KP_Right)) { list = tree_view->priv->columns; while (list) @@ -4236,7 +4347,7 @@ gtk_tree_view_key_press (GtkWidget *widget, } } - if (tree_view->priv->columns && (event->state & GDK_CONTROL_MASK) && + if (tree_view->priv->columns && (event->state & GDK_MOD1_MASK) && (event->keyval == GDK_Left || event->keyval == GDK_KP_Left || event->keyval == GDK_Right || event->keyval == GDK_KP_Right || event->keyval == GDK_Home || event->keyval == GDK_KP_Home @@ -4351,7 +4462,8 @@ gtk_tree_view_key_press (GtkWidget *widget, /* We pass the event to the search_entry. If its text changes, then we start * the typeahead find capabilities. */ - if (tree_view->priv->enable_search) + if (tree_view->priv->enable_search + && !tree_view->priv->search_custom_entry_set) { GdkEvent *new_event; char *old_text; @@ -4359,15 +4471,20 @@ gtk_tree_view_key_press (GtkWidget *widget, gboolean retval; GdkScreen *screen; gboolean text_modified; + gulong popup_menu_id; gtk_tree_view_ensure_interactive_directory (tree_view); /* Make a copy of the current text */ old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry))); new_event = gdk_event_copy ((GdkEvent *) event); - ((GdkEventKey *) new_event)->window = tree_view->priv->search_entry->window; + g_object_unref (((GdkEventKey *) new_event)->window); + ((GdkEventKey *) new_event)->window = g_object_ref (tree_view->priv->search_window->window); gtk_widget_realize (tree_view->priv->search_window); + popup_menu_id = g_signal_connect (tree_view->priv->search_entry, + "popup_menu", G_CALLBACK (gtk_true), NULL); + /* Move the entry off screen */ screen = gtk_widget_get_screen (GTK_WIDGET (tree_view)); gtk_window_move (GTK_WINDOW (tree_view->priv->search_window), @@ -4378,9 +4495,13 @@ gtk_tree_view_key_press (GtkWidget *widget, /* Send the event to the window. If the preedit_changed signal is emitted * during this event, we will set priv->imcontext_changed */ tree_view->priv->imcontext_changed = FALSE; - retval = gtk_widget_event (tree_view->priv->search_entry, new_event); + retval = gtk_widget_event (tree_view->priv->search_window, new_event); + gdk_event_free (new_event); gtk_widget_hide (tree_view->priv->search_window); + g_signal_handler_disconnect (tree_view->priv->search_entry, + popup_menu_id); + /* We check to make sure that the entry tried to handle the text, and that * the text has changed. */ @@ -4455,11 +4576,11 @@ gtk_tree_view_leave_notify (GtkWidget *widget, { GtkTreeView *tree_view; - g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); + tree_view = GTK_TREE_VIEW (widget); + tree_view->priv->pressed_button = -1; if (event->mode == GDK_CROSSING_GRAB) return TRUE; - tree_view = GTK_TREE_VIEW (widget); if (tree_view->priv->prelight_node) _gtk_tree_view_queue_draw_node (tree_view, @@ -4509,7 +4630,7 @@ gtk_tree_view_node_queue_redraw (GtkTreeView *tree_view, gtk_widget_queue_draw_area (GTK_WIDGET (tree_view), 0, y, - GTK_WIDGET (tree_view)->requisition.width, + GTK_WIDGET (tree_view)->allocation.width, GTK_RBNODE_GET_HEIGHT (node)); } @@ -4526,11 +4647,13 @@ validate_row (GtkTreeView *tree_view, GList *list; gint height = 0; gint horizontal_separator; + gint vertical_separator; + gint focus_line_width; gint depth = gtk_tree_path_get_depth (path); gboolean retval = FALSE; gboolean is_separator = FALSE; gint focus_pad; - + /* double check the row needs validating */ if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) && ! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID)) @@ -4545,7 +4668,9 @@ validate_row (GtkTreeView *tree_view, gtk_widget_style_get (GTK_WIDGET (tree_view), "focus-padding", &focus_pad, - "horizontal_separator", &horizontal_separator, + "focus-line-width", &focus_line_width, + "horizontal-separator", &horizontal_separator, + "vertical-separator", &vertical_separator, NULL); for (list = tree_view->priv->columns; list; list = list->next) @@ -4570,6 +4695,7 @@ validate_row (GtkTreeView *tree_view, if (!is_separator) { + tmp_height += vertical_separator; height = MAX (height, tmp_height); height = MAX (height, tree_view->priv->expander_size); } @@ -4635,6 +4761,9 @@ validate_visible_area (GtkTreeView *tree_view) path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path); if (path && !_gtk_tree_view_find_node (tree_view, path, &tree, &node)) { + /* we are going to scroll, and will update dy */ + update_dy = TRUE; + gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID)) @@ -4665,8 +4794,8 @@ validate_visible_area (GtkTreeView *tree_view) dy = _gtk_rbtree_node_find_offset (tree, node); if (dy >= tree_view->priv->vadjustment->value && - dy < (tree_view->priv->vadjustment->value - + tree_view->priv->vadjustment->page_size)) + dy + height <= (tree_view->priv->vadjustment->value + + tree_view->priv->vadjustment->page_size)) { /* row visible: keep the row at the same position */ area_above = dy - tree_view->priv->vadjustment->value; @@ -4764,6 +4893,42 @@ validate_visible_area (GtkTreeView *tree_view) above_path = gtk_tree_path_copy (path); + /* if we do not validate any row above the new top_row, we will make sure + * that the row immediately above top_row has been validated. (if we do not + * do this, _gtk_rbtree_find_offset will find the row above top_row, because + * when invalidated that row's height will be zero. and this will mess up + * scrolling). + */ + if (area_above == 0) + { + GtkRBTree *tree; + GtkRBNode *node; + GtkTreePath *tmppath; + GtkTreeIter iter; + + _gtk_tree_view_find_node (tree_view, above_path, &tree, &node); + + tmppath = gtk_tree_path_copy (above_path); + + _gtk_rbtree_prev_full (tree, node, &tree, &node); + if (! gtk_tree_path_prev (tmppath) && node != NULL) + { + gtk_tree_path_free (tmppath); + tmppath = _gtk_tree_view_find_path (tree_view, tree, node); + } + gtk_tree_model_get_iter (tree_view->priv->model, &iter, tmppath); + + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) || + GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID)) + { + need_redraw = TRUE; + if (validate_row (tree_view, tree, node, &iter, path)) + size_changed = TRUE; + } + + gtk_tree_path_free (tmppath); + } + /* Now, we walk forwards and backwards, measuring rows. Unfortunately, * backwards is much slower then forward, as there is no iter_prev function. * We go forwards first in case we run out of tree. Then we go backwards to @@ -4894,6 +5059,40 @@ validate_visible_area (GtkTreeView *tree_view) update_dy = TRUE; } + /* if we scrolled to a path, we need to set the dy here, + * and sync the top row accordingly + */ + if (tree_view->priv->scroll_to_path) + { + gint dy; + + if (node != NULL) + dy = _gtk_rbtree_node_find_offset (tree, node) - area_above; + else + dy = 0; + + gtk_adjustment_set_value (tree_view->priv->vadjustment, dy); + gtk_tree_view_dy_to_top_row (tree_view); + + need_redraw = TRUE; + } + else if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size) + { + /* when we are not scrolling, we should never set dy to something + * else than zero. we update top_row to be in sync with dy = 0. + */ + gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0); + gtk_tree_view_dy_to_top_row (tree_view); + } + else if (tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size > tree_view->priv->height) + { + gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), tree_view->priv->height - tree_view->priv->vadjustment->page_size); + gtk_tree_view_dy_to_top_row (tree_view); + } + else + gtk_tree_view_top_row_to_dy (tree_view); + + /* update width/height and queue a resize */ if (size_changed) { GtkRequisition requisition; @@ -4901,6 +5100,7 @@ validate_visible_area (GtkTreeView *tree_view) /* We temporarily guess a size, under the assumption that it will be the * same when we get our next size_allocate. If we don't do this, we'll be * in an inconsistent state if we call top_row_to_dy. */ + gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition); tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width); tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height); @@ -4909,35 +5109,6 @@ validate_visible_area (GtkTreeView *tree_view) gtk_widget_queue_resize (GTK_WIDGET (tree_view)); } - /* if we scroll at all, always update dy and kill the top_row */ - if (tree_view->priv->scroll_to_path && - ! GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID)) - { - update_dy = TRUE; - if (tree_view->priv->top_row) - { - gtk_tree_row_reference_free (tree_view->priv->top_row); - tree_view->priv->top_row = NULL; - } - } - - /* if we walk backwards at all, then we need to reset our dy. */ - if (update_dy) - { - gint dy; - if (node != NULL) - { - dy = _gtk_rbtree_node_find_offset (tree, node) - area_above; - } - else - { - dy = 0; - } - - gtk_adjustment_set_value (tree_view->priv->vadjustment, dy); - need_redraw = TRUE; - } - if (tree_view->priv->scroll_to_path) { gtk_tree_row_reference_free (tree_view->priv->scroll_to_path); @@ -4983,7 +5154,7 @@ initialize_fixed_height_mode (GtkTreeView *tree_view) } _gtk_rbtree_set_fixed_height (tree_view->priv->tree, - tree_view->priv->fixed_height); + tree_view->priv->fixed_height, TRUE); } /* Our strategy for finding nodes to validate is a little convoluted. We find @@ -4993,7 +5164,7 @@ initialize_fixed_height_mode (GtkTreeView *tree_view) */ static gboolean -do_validate_rows (GtkTreeView *tree_view) +do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize) { GtkRBTree *tree = NULL; GtkRBNode *node = NULL; @@ -5101,7 +5272,7 @@ do_validate_rows (GtkTreeView *tree_view) if (!tree_view->priv->fixed_height_check) { if (fixed_height) - _gtk_rbtree_set_fixed_height (tree_view->priv->tree, prev_height); + _gtk_rbtree_set_fixed_height (tree_view->priv->tree, prev_height, FALSE); tree_view->priv->fixed_height_check = 1; } @@ -5113,18 +5284,16 @@ do_validate_rows (GtkTreeView *tree_view) /* We temporarily guess a size, under the assumption that it will be the * same when we get our next size_allocate. If we don't do this, we'll be * in an inconsistent state when we call top_row_to_dy. */ + gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition); tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width); tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height); gtk_adjustment_changed (tree_view->priv->hadjustment); gtk_adjustment_changed (tree_view->priv->vadjustment); - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - if (gtk_tree_row_reference_valid (tree_view->priv->top_row)) - gtk_tree_view_top_row_to_dy (tree_view); - else - gtk_tree_view_dy_to_top_row (tree_view); + if (queue_resize) + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } if (path) gtk_tree_path_free (path); @@ -5136,7 +5305,7 @@ validate_rows (GtkTreeView *tree_view) { gboolean retval; - retval = do_validate_rows (tree_view); + retval = do_validate_rows (tree_view, TRUE); if (! retval && tree_view->priv->validate_rows_timer) { @@ -5154,7 +5323,7 @@ validate_rows_handler (GtkTreeView *tree_view) GDK_THREADS_ENTER (); - retval = do_validate_rows (tree_view); + retval = do_validate_rows (tree_view, TRUE); if (! retval && tree_view->priv->validate_rows_timer) { g_source_remove (tree_view->priv->validate_rows_timer); @@ -5177,6 +5346,19 @@ do_presize_handler (GtkTreeView *tree_view) } validate_visible_area (tree_view); tree_view->priv->presize_handler_timer = 0; + + if (tree_view->priv->fixed_height_mode) + { + GtkRequisition requisition; + + gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition); + + tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width); + tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height); + gtk_adjustment_changed (tree_view->priv->hadjustment); + gtk_adjustment_changed (tree_view->priv->vadjustment); + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } return FALSE; } @@ -5217,7 +5399,9 @@ scroll_sync_handler (GtkTreeView *tree_view) GDK_THREADS_ENTER (); - if (gtk_tree_row_reference_valid (tree_view->priv->top_row)) + if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size) + gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0); + else if (gtk_tree_row_reference_valid (tree_view->priv->top_row)) gtk_tree_view_top_row_to_dy (tree_view); else gtk_tree_view_dy_to_top_row (tree_view); @@ -5355,7 +5539,7 @@ set_source_row (GdkDragContext *context, GtkTreePath *source_row) { g_object_set_data_full (G_OBJECT (context), - "gtk-tree-view-source-row", + I_("gtk-tree-view-source-row"), source_row ? gtk_tree_row_reference_new (model, source_row) : NULL, (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL)); } @@ -5403,7 +5587,7 @@ set_dest_row (GdkDragContext *context, if (!dest_row) { - g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row", + g_object_set_data_full (G_OBJECT (context), I_("gtk-tree-view-dest-row"), NULL, NULL); return; } @@ -5415,7 +5599,7 @@ set_dest_row (GdkDragContext *context, dr->empty_view_drop = empty_view_drop; dr->drop_append_mode = drop_append_mode; - g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row", + g_object_set_data_full (G_OBJECT (context), I_("gtk-tree-view-dest-row"), dr, (GDestroyNotify) dest_row_free); } @@ -5458,7 +5642,7 @@ set_status_pending (GdkDragContext *context, GdkDragAction suggested_action) { g_object_set_data (G_OBJECT (context), - "gtk-tree-view-status-pending", + I_("gtk-tree-view-status-pending"), GINT_TO_POINTER (suggested_action)); } @@ -5513,7 +5697,7 @@ ensure_info (GtkTreeView *tree_view) di = g_new0 (TreeViewDragInfo, 1); g_object_set_data_full (G_OBJECT (tree_view), - "gtk-tree-view-drag-info", + I_("gtk-tree-view-drag-info"), di, (GDestroyNotify) destroy_info); } @@ -5524,7 +5708,7 @@ ensure_info (GtkTreeView *tree_view) static void remove_info (GtkTreeView *tree_view) { - g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL); + g_object_set_data (G_OBJECT (tree_view), I_("gtk-tree-view-drag-info"), NULL); } #if 0 @@ -5908,9 +6092,8 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view, path)) goto out; - /* FIXME Check whether we're a start button, if not return FALSE and - * free path - */ + if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask)) + goto out; /* Now we can begin the drag */ @@ -6021,7 +6204,7 @@ gtk_tree_view_drag_data_get (GtkWidget *widget, goto done; /* If drag_data_get does nothing, try providing row data. */ - if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE)) + if (selection_data->target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW")) { gtk_tree_set_row_drag_data (selection_data, model, @@ -6071,10 +6254,6 @@ gtk_tree_view_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time) { - TreeViewDragInfo *di; - - di = get_info (GTK_TREE_VIEW (widget)); - /* unset any highlight row */ gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), NULL, @@ -6094,7 +6273,6 @@ gtk_tree_view_drag_motion (GtkWidget *widget, { gboolean empty; GtkTreePath *path = NULL; - GtkTreeModel *model; GtkTreeViewDropPosition pos; GtkTreeView *tree_view; GdkDragAction suggested_action = 0; @@ -6108,7 +6286,6 @@ gtk_tree_view_drag_motion (GtkWidget *widget, gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); /* we only know this *after* set_desination_row */ - model = gtk_tree_view_get_model (tree_view); empty = tree_view->priv->empty_view_drop; if (path == NULL && !empty) @@ -6131,7 +6308,7 @@ gtk_tree_view_drag_motion (GtkWidget *widget, g_timeout_add (150, scroll_row_timeout, tree_view); } - if (target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE)) + if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW")) { /* Request data so we can use the source row when * determining whether to accept the drop @@ -6457,7 +6634,7 @@ column_sizing_notify (GObject *object, if (gtk_tree_view_column_get_sizing (c) != GTK_TREE_VIEW_COLUMN_FIXED) /* disable fixed height mode */ - g_object_set (data, "fixed_height_mode", FALSE, NULL); + g_object_set (data, "fixed-height-mode", FALSE, NULL); } /** @@ -6541,7 +6718,6 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view, GtkDirectionType dir) { GtkWidget *focus_child; - GtkContainer *container; GList *last_column, *first_column; GList *tmp_list; @@ -6551,7 +6727,6 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view, return FALSE; focus_child = GTK_CONTAINER (tree_view)->focus_child; - container = GTK_CONTAINER (tree_view); first_column = tree_view->priv->columns; while (first_column) @@ -6680,9 +6855,10 @@ gtk_tree_view_header_focus (GtkTreeView *tree_view, { for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child) - break; - - tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + { + tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + break; + } /* If the following isn't true, then the view is smaller then the scrollpane. */ @@ -6792,7 +6968,7 @@ gtk_tree_view_style_set (GtkWidget *widget, } gtk_widget_style_get (widget, - "expander_size", &tree_view->priv->expander_size, + "expander-size", &tree_view->priv->expander_size, NULL); tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING; @@ -6865,8 +7041,7 @@ gtk_tree_view_set_adjustments (GtkTreeView *tree_view, if (tree_view->priv->hadjustment != hadj) { tree_view->priv->hadjustment = hadj; - g_object_ref (tree_view->priv->hadjustment); - gtk_object_sink (GTK_OBJECT (tree_view->priv->hadjustment)); + g_object_ref_sink (tree_view->priv->hadjustment); g_signal_connect (tree_view->priv->hadjustment, "value_changed", G_CALLBACK (gtk_tree_view_adjustment_changed), @@ -6877,8 +7052,7 @@ gtk_tree_view_set_adjustments (GtkTreeView *tree_view, if (tree_view->priv->vadjustment != vadj) { tree_view->priv->vadjustment = vadj; - g_object_ref (tree_view->priv->vadjustment); - gtk_object_sink (GTK_OBJECT (tree_view->priv->vadjustment)); + g_object_ref_sink (tree_view->priv->vadjustment); g_signal_connect (tree_view->priv->vadjustment, "value_changed", G_CALLBACK (gtk_tree_view_adjustment_changed), @@ -7029,6 +7203,7 @@ gtk_tree_view_row_changed (GtkTreeModel *model, gboolean free_path = FALSE; gint vertical_separator; GList *list; + GtkTreePath *cursor_path; g_return_if_fail (path != NULL || iter != NULL); @@ -7037,10 +7212,19 @@ gtk_tree_view_row_changed (GtkTreeModel *model, */ return; - if (tree_view->priv->edited_column) + if (tree_view->priv->cursor != NULL) + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + else + cursor_path = NULL; + + if (tree_view->priv->edited_column && + (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0)) gtk_tree_view_stop_editing (tree_view, TRUE); - gtk_widget_style_get (GTK_WIDGET (data), "vertical_separator", &vertical_separator, NULL); + if (cursor_path != NULL) + gtk_tree_path_free (cursor_path); + + gtk_widget_style_get (GTK_WIDGET (data), "vertical-separator", &vertical_separator, NULL); if (path == NULL) { @@ -7060,6 +7244,11 @@ gtk_tree_view_row_changed (GtkTreeModel *model, if (tree == NULL) goto done; + /* Check if the node became insensitive, and if so, unselect it */ + if (!_gtk_tree_selection_row_is_selectable (tree_view->priv->selection, + node, path)) + gtk_tree_selection_unselect_path (tree_view->priv->selection, path); + if (tree_view->priv->fixed_height_mode && tree_view->priv->fixed_height >= 0) { @@ -7182,11 +7371,15 @@ gtk_tree_view_row_inserted (GtkTreeModel *model, { tmpnode = _gtk_rbtree_find_count (tree, indices[depth - 1]); _gtk_rbtree_insert_after (tree, tmpnode, height, FALSE); - } + } done: if (height > 0) - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + { + if (tree) + _gtk_rbtree_node_mark_valid (tree, tmpnode); + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } else install_presize_handler (tree_view); if (free_path) @@ -7529,7 +7722,7 @@ gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view, } gtk_widget_style_get (GTK_WIDGET (tree_view), - "indent_expanders", &indent_expanders, + "indent-expanders", &indent_expanders, NULL); if (indent_expanders) @@ -7539,23 +7732,14 @@ gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view, else x_offset += tree_view->priv->expander_size * _gtk_rbtree_get_depth (tree); } - if (x1) - { - *x1 = x_offset; - } + *x1 = x_offset; + if (tmp_column && tmp_column->visible) - { - /* +1 because x2 isn't included in the range. */ - if (x2) - *x2 = *x1 + tree_view->priv->expander_size + 1; - } + /* +1 because x2 isn't included in the range. */ + *x2 = *x1 + tree_view->priv->expander_size + 1; else - { - /* return an empty range, the expander column is hidden */ - if (x2) - *x2 = *x1; - } + *x2 = *x1; } static void @@ -7573,6 +7757,15 @@ gtk_tree_view_build_tree (GtkTreeView *tree_view, gtk_tree_model_ref_node (tree_view->priv->model, iter); temp = _gtk_rbtree_insert_after (tree, temp, 0, FALSE); + if (tree_view->priv->fixed_height > 0) + { + if (GTK_RBNODE_FLAG_SET (temp, GTK_RBNODE_INVALID)) + { + _gtk_rbtree_node_set_height (tree, temp, tree_view->priv->fixed_height); + _gtk_rbtree_node_mark_valid (tree, temp); + } + } + if (is_list) continue; @@ -7614,7 +7807,7 @@ gtk_tree_view_discover_dirty_iter (GtkTreeView *tree_view, gint horizontal_separator; gtk_widget_style_get (GTK_WIDGET (tree_view), - "horizontal_separator", &horizontal_separator, + "horizontal-separator", &horizontal_separator, NULL); if (height) @@ -7725,13 +7918,22 @@ gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, GtkRBTree *tree, GtkRBNode *node) { + gint node_dy, height; GtkTreePath *path = NULL; if (!GTK_WIDGET_REALIZED (tree_view)) return; - path = _gtk_tree_view_find_path (tree_view, tree, node); + /* just return if the node is visible, avoiding a costly expose */ + node_dy = _gtk_rbtree_node_find_offset (tree, node); + height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node)); + if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) + && node_dy >= tree_view->priv->vadjustment->value + && node_dy + height <= (tree_view->priv->vadjustment->value + + tree_view->priv->vadjustment->page_size)) + return; + path = _gtk_tree_view_find_path (tree_view, tree, node); if (path) { /* We process updates because we want to clear old selected items when we scroll. @@ -7879,6 +8081,7 @@ static void gtk_tree_view_add_move_binding (GtkBindingSet *binding_set, guint keyval, guint modmask, + gboolean add_shifted_binding, GtkMovementStep step, gint count) { @@ -7888,10 +8091,11 @@ gtk_tree_view_add_move_binding (GtkBindingSet *binding_set, G_TYPE_ENUM, step, G_TYPE_INT, count); - gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK, - "move_cursor", 2, - G_TYPE_ENUM, step, - G_TYPE_INT, count); + if (add_shifted_binding) + gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK, + "move_cursor", 2, + G_TYPE_ENUM, step, + G_TYPE_INT, count); if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) return; @@ -8021,7 +8225,7 @@ gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view, /* Add the last one */ if (tree_view->priv->column_drop_func == NULL || ((left_column != column) && - (* tree_view->priv->column_drop_func) (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))) + (* tree_view->priv->column_drop_func) (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))) { reorder = g_new (GtkTreeViewColumnReorder, 1); reorder->left_column = left_column; @@ -8267,12 +8471,13 @@ gtk_tree_view_draw_arrow (GtkTreeView *tree_view, GtkStateType state; GtkWidget *widget; gint x_offset = 0; + gint x2; gint vertical_separator; gint expander_size; GtkExpanderStyle expander_style; gtk_widget_style_get (GTK_WIDGET (tree_view), - "vertical_separator", &vertical_separator, + "vertical-separator", &vertical_separator, NULL); expander_size = tree_view->priv->expander_size - EXPANDER_EXTRA_PADDING; @@ -8281,14 +8486,18 @@ gtk_tree_view_draw_arrow (GtkTreeView *tree_view, widget = GTK_WIDGET (tree_view); - gtk_tree_view_get_arrow_xrange (tree_view, tree, &x_offset, NULL); + gtk_tree_view_get_arrow_xrange (tree_view, tree, &x_offset, &x2); area.x = x_offset; area.y = CELL_FIRST_PIXEL (tree_view, tree, node, vertical_separator); area.width = expander_size + 2; area.height = MAX (CELL_HEIGHT (node, vertical_separator), (expander_size - vertical_separator)); - if (node == tree_view->priv->button_pressed_node) + if (GTK_WIDGET_STATE (tree_view) == GTK_STATE_INSENSITIVE) + { + state = GTK_STATE_INSENSITIVE; + } + else if (node == tree_view->priv->button_pressed_node) { if (x >= area.x && x <= (area.x + area.width) && y >= area.y && y <= (area.y + area.height)) @@ -8464,6 +8673,7 @@ gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view, GtkRBNode *cursor_node = NULL; GtkTreePath *cursor_path = NULL; gint y; + gint window_y; gint vertical_separator; if (! GTK_WIDGET_HAS_FOCUS (tree_view)) @@ -8475,7 +8685,7 @@ gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view, /* This is sorta weird. Focus in should give us a cursor */ return; - gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical_separator", &vertical_separator, NULL); + gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL); _gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node); @@ -8487,22 +8697,21 @@ gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view, g_return_if_fail (cursor_node != NULL); y = _gtk_rbtree_node_find_offset (cursor_tree, cursor_node); - y += count * tree_view->priv->vadjustment->page_size; - if (count > 0) - y -= ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (cursor_node)); - else if (count < 0) - y += ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (cursor_node)); + window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y); + y += count * (int)tree_view->priv->vadjustment->page_increment; y = CLAMP (y, (gint)tree_view->priv->vadjustment->lower, (gint)tree_view->priv->vadjustment->upper - vertical_separator); if (y >= tree_view->priv->height) y = tree_view->priv->height - 1; - _gtk_rbtree_find_offset (tree_view->priv->tree, y, &cursor_tree, &cursor_node); + y -= _gtk_rbtree_find_offset (tree_view->priv->tree, y, &cursor_tree, &cursor_node); cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node); g_return_if_fail (cursor_path != NULL); - gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE); - gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); + gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE); gtk_tree_path_free (cursor_path); + + y -= window_y; + gtk_tree_view_scroll_to_point (tree_view, -1, y); } static void @@ -8807,6 +9016,7 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view) GtkRBTree *cursor_tree = NULL; GtkRBNode *cursor_node = NULL; GtkTreePath *cursor_path = NULL; + GdkModifierType state; if (! GTK_WIDGET_HAS_FOCUS (tree_view)) return FALSE; @@ -8826,6 +9036,12 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view) return FALSE; } + if (gtk_get_current_event_state (&state)) + { + if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) + tree_view->priv->ctrl_pressed = TRUE; + } + if (cursor_tree->parent_node) { gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); @@ -8833,14 +9049,8 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view) cursor_tree = cursor_tree->parent_tree; gtk_tree_path_up (cursor_path); - gtk_tree_row_reference_free (tree_view->priv->cursor); - tree_view->priv->cursor = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, cursor_path); - _gtk_tree_selection_internal_select_node (tree_view->priv->selection, - cursor_node, - cursor_tree, - cursor_path, - 0, - FALSE); + + gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE); } gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); @@ -8849,8 +9059,11 @@ gtk_tree_view_real_select_cursor_parent (GtkTreeView *tree_view) gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); gtk_tree_path_free (cursor_path); + tree_view->priv->ctrl_pressed = FALSE; + return TRUE; } + static gboolean gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view) { @@ -8861,7 +9074,7 @@ gtk_tree_view_search_entry_flush_timeout (GtkTreeView *tree_view) GDK_THREADS_LEAVE (); - return TRUE; + return FALSE; } /* Cut and paste from gtkwindow.c */ @@ -8884,7 +9097,7 @@ send_focus_change (GtkWidget *widget, gtk_widget_event (widget, fevent); - g_object_notify (G_OBJECT (widget), "has_focus"); + g_object_notify (G_OBJECT (widget), "has-focus"); g_object_unref (widget); gdk_event_free (fevent); @@ -8893,13 +9106,30 @@ send_focus_change (GtkWidget *widget, static void gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view) { - GtkWidget *frame, *vbox; + GtkWidget *frame, *vbox, *toplevel; - if (tree_view->priv->search_window != NULL) + if (tree_view->priv->search_custom_entry_set) return; + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view)); + + if (tree_view->priv->search_window != NULL) + { + if (GTK_WINDOW (toplevel)->group) + gtk_window_group_add_window (GTK_WINDOW (toplevel)->group, + GTK_WINDOW (tree_view->priv->search_window)); + else if (GTK_WINDOW (tree_view->priv->search_window)->group) + gtk_window_group_remove_window (GTK_WINDOW (tree_view->priv->search_window)->group, + GTK_WINDOW (tree_view->priv->search_window)); + return; + } + tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP); + if (GTK_WINDOW (toplevel)->group) + gtk_window_group_add_window (GTK_WINDOW (toplevel)->group, + GTK_WINDOW (tree_view->priv->search_window)); + gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE); g_signal_connect (tree_view->priv->search_window, "delete_event", G_CALLBACK (gtk_tree_view_search_delete_event), @@ -8910,6 +9140,9 @@ gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view) g_signal_connect (tree_view->priv->search_window, "button_press_event", G_CALLBACK (gtk_tree_view_search_button_press_event), tree_view); + g_signal_connect (tree_view->priv->search_window, "scroll_event", + G_CALLBACK (gtk_tree_view_search_scroll_event), + tree_view); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); @@ -8955,7 +9188,14 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view, gboolean found_focus = FALSE; GtkWidgetClass *entry_parent_class; - if (GTK_WIDGET_VISIBLE (tree_view->priv->search_window)) + if (!tree_view->priv->enable_search && !keybinding) + return FALSE; + + if (tree_view->priv->search_custom_entry_set) + return FALSE; + + if (tree_view->priv->search_window != NULL && + GTK_WIDGET_VISIBLE (tree_view->priv->search_window)) return TRUE; for (list = tree_view->priv->columns; list; list = list->next) @@ -8976,22 +9216,19 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view, if (GTK_WIDGET_HAS_FOCUS (tree_view)) found_focus = TRUE; - if (! found_focus) + if (!found_focus) return FALSE; - if (tree_view->priv->enable_search == FALSE || - tree_view->priv->search_column < 0) + if (tree_view->priv->search_column < 0) return FALSE; gtk_tree_view_ensure_interactive_directory (tree_view); if (keybinding) - { - gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), ""); - } + gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), ""); /* done, show it */ - tree_view->priv->search_dialog_position_func (tree_view, tree_view->priv->search_window); + tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data); gtk_widget_show (tree_view->priv->search_window); if (tree_view->priv->search_entry_changed_id == 0) { @@ -9000,13 +9237,11 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view, G_CALLBACK (gtk_tree_view_search_init), tree_view); } - if (! keybinding) - { - tree_view->priv->typeselect_flush_timeout = - g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, - (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, - tree_view); - } + + tree_view->priv->typeselect_flush_timeout = + g_timeout_add (GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT, + (GSourceFunc) gtk_tree_view_search_entry_flush_timeout, + tree_view); /* Grab focus will select all the text. We don't want that to happen, so we * call the parent instance and bypass the selection change. This is probably @@ -9026,8 +9261,9 @@ gtk_tree_view_real_start_interactive_search (GtkTreeView *tree_view, static gboolean gtk_tree_view_start_interactive_search (GtkTreeView *tree_view) { - return gtk_tree_view_real_start_interactive_search (tree_view, FALSE); + return gtk_tree_view_real_start_interactive_search (tree_view, TRUE); } + /* this function returns the new width of the column being resized given * the column and x position of the cursor; the x cursor position is passed * in as a pointer and automagicly corrected if it's beyond min/max limits @@ -9056,7 +9292,7 @@ gtk_tree_view_new_column_width (GtkTreeView *tree_view, width = MAX (column->min_width, width); if (column->max_width != -1) - width = MIN (width, 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); @@ -9220,7 +9456,7 @@ gtk_tree_view_new_with_model (GtkTreeModel *model) * gtk_tree_view_get_model: * @tree_view: a #GtkTreeView * - * Returns the model the the #GtkTreeView is based on. Returns %NULL if the + * Returns the model the #GtkTreeView is based on. Returns %NULL if the * model is unset. * * Return value: A #GtkTreeModel, or %NULL if none is currently being used. @@ -9254,6 +9490,12 @@ gtk_tree_view_set_model (GtkTreeView *tree_view, if (model == tree_view->priv->model) return; + if (tree_view->priv->scroll_to_path) + { + gtk_tree_row_reference_free (tree_view->priv->scroll_to_path); + tree_view->priv->scroll_to_path = NULL; + } + if (tree_view->priv->model) { GList *tmplist = tree_view->priv->columns; @@ -9282,15 +9524,7 @@ gtk_tree_view_set_model (GtkTreeView *tree_view, tree_view->priv->model); if (tree_view->priv->tree) - { - _gtk_rbtree_free (tree_view->priv->tree); - tree_view->priv->tree = NULL; - } - - tree_view->priv->prelight_node = NULL; - tree_view->priv->prelight_tree = NULL; - tree_view->priv->button_pressed_node = NULL; - tree_view->priv->button_pressed_tree = NULL; + gtk_tree_view_free_rbtree (tree_view); gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); tree_view->priv->drag_dest_row = NULL; @@ -9319,7 +9553,6 @@ gtk_tree_view_set_model (GtkTreeView *tree_view, tree_view->priv->model = model; - if (tree_view->priv->model) { gint i; @@ -9383,6 +9616,9 @@ gtk_tree_view_set_model (GtkTreeView *tree_view, g_object_notify (G_OBJECT (tree_view), "model"); + if (tree_view->priv->selection) + _gtk_tree_selection_emit_changed (tree_view->priv->selection); + if (GTK_WIDGET_REALIZED (tree_view)) gtk_widget_queue_resize (GTK_WIDGET (tree_view)); } @@ -9506,7 +9742,7 @@ gtk_tree_view_get_headers_visible (GtkTreeView *tree_view) * @tree_view: A #GtkTreeView. * @headers_visible: %TRUE if the headers are visible * - * Sets the the visibility state of the headers. + * Sets the visibility state of the headers. **/ void gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, @@ -9559,7 +9795,7 @@ gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - g_object_notify (G_OBJECT (tree_view), "headers_visible"); + g_object_notify (G_OBJECT (tree_view), "headers-visible"); } /** @@ -9605,15 +9841,38 @@ gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view, GList *list; g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (tree_view->priv->model != NULL); for (list = tree_view->priv->columns; list; list = list->next) gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (list->data), setting); - g_object_notify (G_OBJECT (tree_view), "headers_clickable"); + g_object_notify (G_OBJECT (tree_view), "headers-clickable"); } +/** + * gtk_tree_view_get_headers_clickable: + * @tree_view: A #GtkTreeView. + * + * Returns whether all header columns are clickable. + * + * Return value: %TRUE if all header columns are clickable, otherwise %FALSE + * + * Since: 2.10 + **/ +gboolean +gtk_tree_view_get_headers_clickable (GtkTreeView *tree_view) +{ + GList *list; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + for (list = tree_view->priv->columns; list; list = list->next) + if (!GTK_TREE_VIEW_COLUMN (list->data)->clickable) + return FALSE; + + return TRUE; +} + /** * gtk_tree_view_set_rules_hint * @tree_view: a #GtkTreeView @@ -9647,7 +9906,7 @@ gtk_tree_view_set_rules_hint (GtkTreeView *tree_view, gtk_widget_queue_draw (GTK_WIDGET (tree_view)); } - g_object_notify (G_OBJECT (tree_view), "rules_hint"); + g_object_notify (G_OBJECT (tree_view), "rules-hint"); } /** @@ -9782,8 +10041,7 @@ gtk_tree_view_insert_column (GtkTreeView *tree_view, g_return_val_if_fail (gtk_tree_view_column_get_sizing (column) == GTK_TREE_VIEW_COLUMN_FIXED, -1); - g_object_ref (column); - gtk_object_sink (GTK_OBJECT (column)); + g_object_ref_sink (column); if (tree_view->priv->n_columns == 0 && GTK_WIDGET_REALIZED (tree_view) && @@ -10021,9 +10279,12 @@ gtk_tree_view_move_column_after (GtkTreeView *tree_view, * @tree_view: A #GtkTreeView * @column: %NULL, or the column to draw the expander arrow at. * - * Sets the column to draw the expander arrow at. It must be in @tree_view. If - * @column is %NULL, then the expander arrow is always at the first visible - * column. + * Sets the column to draw the expander arrow at. It must be in @tree_view. + * If @column is %NULL, then the expander arrow is always at the first + * visible column. + * + * If you do not want expander arrow to appear in your tree, set the + * expander column to a hidden column. **/ void gtk_tree_view_set_expander_column (GtkTreeView *tree_view, @@ -10047,7 +10308,7 @@ gtk_tree_view_set_expander_column (GtkTreeView *tree_view, } tree_view->priv->expander_column = column; - g_object_notify (G_OBJECT (tree_view), "expander_column"); + g_object_notify (G_OBJECT (tree_view), "expander-column"); } } @@ -10194,7 +10455,9 @@ gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, * scrolling code, we short-circuit validate_visible_area's immplementation as * it is much slower than just going to the point. */ - if (! GTK_WIDGET_REALIZED (tree_view) || + if (! GTK_WIDGET_VISIBLE (tree_view) || + ! GTK_WIDGET_REALIZED (tree_view) || + GTK_WIDGET_ALLOC_NEEDED (tree_view) || GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID)) { if (tree_view->priv->scroll_to_path) @@ -10308,48 +10571,6 @@ gtk_tree_view_expand_all_emission_helper (GtkRBTree *tree, gtk_tree_view_expand_all_emission_helper, tree_view); } - -static void -gtk_tree_view_expand_all_helper (GtkRBTree *tree, - GtkRBNode *node, - gpointer data) -{ - GtkTreeView *tree_view = data; - - if (node->children) - _gtk_rbtree_traverse (node->children, - node->children->root, - G_PRE_ORDER, - gtk_tree_view_expand_all_helper, - data); - else if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT && node->children == NULL) - { - GtkTreePath *path; - GtkTreeIter iter; - GtkTreeIter child; - - node->children = _gtk_rbtree_new (); - node->children->parent_tree = tree; - node->children->parent_node = node; - path = _gtk_tree_view_find_path (tree_view, tree, node); - gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - gtk_tree_model_iter_children (tree_view->priv->model, &child, &iter); - gtk_tree_view_build_tree (tree_view, - node->children, - &child, - gtk_tree_path_get_depth (path) + 1, - TRUE); - - g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path); - _gtk_rbtree_traverse (node->children, - node->children->root, - G_PRE_ORDER, - gtk_tree_view_expand_all_emission_helper, - tree_view); - gtk_tree_path_free (path); - } -} - /** * gtk_tree_view_expand_all: * @tree_view: A #GtkTreeView. @@ -10359,16 +10580,26 @@ gtk_tree_view_expand_all_helper (GtkRBTree *tree, void gtk_tree_view_expand_all (GtkTreeView *tree_view) { + GtkTreePath *path; + GtkRBTree *tree; + GtkRBNode *node; + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); if (tree_view->priv->tree == NULL) return; - _gtk_rbtree_traverse (tree_view->priv->tree, - tree_view->priv->tree->root, - G_PRE_ORDER, - gtk_tree_view_expand_all_helper, - tree_view); + path = gtk_tree_path_new_first (); + _gtk_tree_view_find_node (tree_view, path, &tree, &node); + + while (node) + { + gtk_tree_view_real_expand_row (tree_view, path, tree, node, TRUE, FALSE); + node = _gtk_rbtree_next (tree, node); + gtk_tree_path_next (path); + } + + gtk_tree_path_free (path); } /* Timeout to animate the expander during expands and collapses */ @@ -10541,6 +10772,11 @@ gtk_tree_view_real_expand_row (GtkTreeView *tree_view, GtkTreeIter temp; gboolean expand; + if (animate) + g_object_get (gtk_widget_get_settings (GTK_WIDGET (tree_view)), + "gtk-enable-animations", &animate, + NULL); + remove_auto_expand_timeout (tree_view); if (node->children && !open_all) @@ -10626,6 +10862,14 @@ gtk_tree_view_real_expand_row (GtkTreeView *tree_view, install_presize_handler (tree_view); g_signal_emit (tree_view, tree_view_signals[ROW_EXPANDED], 0, &iter, path); + if (open_all) + { + _gtk_rbtree_traverse (node->children, + node->children->root, + G_PRE_ORDER, + gtk_tree_view_expand_all_emission_helper, + tree_view); + } return TRUE; } @@ -10676,9 +10920,13 @@ gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, gboolean collapse; gint x, y; GList *list; - GdkDisplay *display; GdkWindow *child, *parent; + if (animate) + g_object_get (gtk_widget_get_settings (GTK_WIDGET (tree_view)), + "gtk-enable-animations", &animate, + NULL); + remove_auto_expand_timeout (tree_view); if (node->children == NULL) @@ -10784,6 +11032,14 @@ gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, gtk_tree_path_free (lsc); } + if (tree_view->priv->expanded_collapsed_node != NULL) + { + GTK_RBNODE_UNSET_FLAG (tree_view->priv->expanded_collapsed_node, GTK_RBNODE_IS_SEMI_EXPANDED); + GTK_RBNODE_UNSET_FLAG (tree_view->priv->expanded_collapsed_node, GTK_RBNODE_IS_SEMI_COLLAPSED); + + tree_view->priv->expanded_collapsed_node = NULL; + } + if (gtk_tree_view_unref_and_check_selection_tree (tree_view, node->children)) { _gtk_rbtree_remove (node->children); @@ -10798,14 +11054,6 @@ gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, tree_view->priv->expand_collapse_timeout = 0; } - if (tree_view->priv->expanded_collapsed_node != NULL) - { - GTK_RBNODE_UNSET_FLAG (tree_view->priv->expanded_collapsed_node, GTK_RBNODE_IS_SEMI_EXPANDED); - GTK_RBNODE_UNSET_FLAG (tree_view->priv->expanded_collapsed_node, GTK_RBNODE_IS_SEMI_COLLAPSED); - - tree_view->priv->expanded_collapsed_node = NULL; - } - if (animate) { tree_view->priv->expand_collapse_timeout = g_timeout_add (50, expand_collapse_timeout, tree_view); @@ -10827,7 +11075,6 @@ gtk_tree_view_real_collapse_row (GtkTreeView *tree_view, /* now that we've collapsed all rows, we want to try to set the prelight * again. To do this, we fake a motion event and send it to ourselves. */ - display = gdk_drawable_get_display (tree_view->priv->bin_window); child = tree_view->priv->bin_window; parent = gdk_window_get_parent (child); @@ -11306,8 +11553,12 @@ gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, GList *list; gint remaining_x = x; gboolean found = FALSE; + gboolean rtl; - for (list = tree_view->priv->columns; list; list = list->next) + 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)) { tmp_column = list->data; @@ -11402,8 +11653,8 @@ gtk_tree_view_get_cell_area (GtkTreeView *tree_view, g_return_if_fail (GTK_WIDGET_REALIZED (tree_view)); gtk_widget_style_get (GTK_WIDGET (tree_view), - "vertical_separator", &vertical_separator, - "horizontal_separator", &horizontal_separator, + "vertical-separator", &vertical_separator, + "horizontal-separator", &horizontal_separator, NULL); rect->x = 0; @@ -11599,6 +11850,58 @@ gtk_tree_view_tree_to_widget_coords (GtkTreeView *tree_view, *wy = ty - tree_view->priv->dy; } +/** + * gtk_tree_view_get_visible_range: + * @tree_view: A #GtkTreeView + * @start_path: Return location for start of region, or %NULL. + * @end_path: Return location for end of region, or %NULL. + * + * Sets @start_path and @end_path to be the first and last visible path. + * Note that there may be invisible paths in between. + * + * The paths should be freed with gtk_tree_path_free() after use. + * + * Returns: %TRUE, if valid paths were placed in @start_path and @end_path. + * + * Since: 2.8 + **/ +gboolean +gtk_tree_view_get_visible_range (GtkTreeView *tree_view, + GtkTreePath **start_path, + GtkTreePath **end_path) +{ + GtkRBTree *tree; + GtkRBNode *node; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + if (!tree_view->priv->tree) + return FALSE; + + if (start_path) + { + _gtk_rbtree_find_offset (tree_view->priv->tree, + TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0), + &tree, &node); + *start_path = _gtk_tree_view_find_path (tree_view, tree, node); + } + + if (end_path) + { + gint y; + + if (tree_view->priv->height < tree_view->priv->vadjustment->page_size) + y = tree_view->priv->height - 1; + else + y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, tree_view->priv->vadjustment->page_size) - 1; + + _gtk_rbtree_find_offset (tree_view->priv->tree, y, &tree, &node); + *end_path = _gtk_tree_view_find_path (tree_view, tree, node); + } + + return TRUE; +} + static void unset_reorderable (GtkTreeView *tree_view) { @@ -11633,8 +11936,8 @@ gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view, gtk_drag_source_set (GTK_WIDGET (tree_view), 0, - NULL, - 0, + targets, + n_targets, actions); di = ensure_info (tree_view); @@ -11671,8 +11974,8 @@ gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view, gtk_drag_dest_set (GTK_WIDGET (tree_view), 0, - NULL, - 0, + targets, + n_targets, actions); di = ensure_info (tree_view); @@ -11868,7 +12171,7 @@ gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, * * Determines the destination row for a given position. * - * Return value: whether there is a row at the given postiion, + * Return value: whether there is a row at the given position. **/ gboolean gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, @@ -11959,8 +12262,8 @@ gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, * @tree_view: a #GtkTreeView * @path: a #GtkTreePath in @tree_view * - * Creates a #GdkPixmap representation of the row at @path. This image is used - * for a drag icon. + * Creates a #GdkPixmap representation of the row at @path. + * This image is used for a drag icon. * * Return value: a newly-allocated pixmap of the drag icon. **/ @@ -11983,8 +12286,14 @@ gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, gint bin_window_width; gboolean is_separator = FALSE; + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + g_return_val_if_fail (path != NULL, NULL); + widget = GTK_WIDGET (tree_view); + if (!GTK_WIDGET_REALIZED (tree_view)) + return NULL; + depth = gtk_tree_path_get_depth (path); _gtk_tree_view_find_node (tree_view, @@ -12050,7 +12359,7 @@ gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, cell_area = background_area; - gtk_widget_style_get (widget, "vertical_separator", &vertical_separator, NULL); + gtk_widget_style_get (widget, "vertical-separator", &vertical_separator, NULL); cell_area.y += vertical_separator / 2; cell_area.height -= vertical_separator; @@ -12133,7 +12442,10 @@ gtk_tree_view_set_destroy_count_func (GtkTreeView *tree_view, * @enable_search: %TRUE, if the user can search interactively * * If @enable_search is set, then the user can type in text to search through - * the tree interactively. + * the tree interactively (this is sometimes called "typeahead find"). + * + * Note that even if this is %FALSE, the user can still initiate a search + * using the "start-interactive-search" key binding. */ void gtk_tree_view_set_enable_search (GtkTreeView *tree_view, @@ -12146,7 +12458,7 @@ gtk_tree_view_set_enable_search (GtkTreeView *tree_view, if (tree_view->priv->enable_search != enable_search) { tree_view->priv->enable_search = enable_search; - g_object_notify (G_OBJECT (tree_view), "enable_search"); + g_object_notify (G_OBJECT (tree_view), "enable-search"); } } @@ -12154,7 +12466,8 @@ gtk_tree_view_set_enable_search (GtkTreeView *tree_view, * gtk_tree_view_get_enable_search: * @tree_view: A #GtkTreeView * - * Returns whether or not the tree allows interactive searching. + * Returns whether or not the tree allows to start interactive searching + * by typing in text. * * Return value: whether or not to let the user search interactively */ @@ -12178,7 +12491,7 @@ gtk_tree_view_get_enable_search (GtkTreeView *tree_view) gint gtk_tree_view_get_search_column (GtkTreeView *tree_view) { - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0); + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); return (tree_view->priv->search_column); } @@ -12186,24 +12499,29 @@ gtk_tree_view_get_search_column (GtkTreeView *tree_view) /** * gtk_tree_view_set_search_column: * @tree_view: A #GtkTreeView - * @column: the column of the model to search in + * @column: the column of the model to search in, or -1 to disable searching * * Sets @column as the column where the interactive search code should - * search in. Additionally, turns on interactive searching. Note that - * @column refers to a column of the model. + * search in. + * + * If the sort column is set, users can use the "start-interactive-search" + * key binding to bring up search popup. The enable-search property controls + * whether simply typing text will also start an interactive search. + * + * Note that @column refers to a column of the model. */ void gtk_tree_view_set_search_column (GtkTreeView *tree_view, gint column) { g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (column >= 0); + g_return_if_fail (column >= -1); if (tree_view->priv->search_column == column) return; tree_view->priv->search_column = column; - g_object_notify (G_OBJECT (tree_view), "search_column"); + g_object_notify (G_OBJECT (tree_view), "search-column"); } /** @@ -12253,6 +12571,146 @@ gtk_tree_view_set_search_equal_func (GtkTreeView *tree_view, tree_view->priv->search_equal_func = gtk_tree_view_search_equal_func; } +/** + * gtk_tree_view_get_search_entry: + * @tree_view: A #GtkTreeView + * + * Returns the GtkEntry which is currently in use as interactive search + * entry for @tree_view. In case the built-in entry is being used, %NULL + * will be returned. + * + * Return value: the entry currently in use as search entry. + * + * Since: 2.10 + */ +GtkEntry * +gtk_tree_view_get_search_entry (GtkTreeView *tree_view) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + if (tree_view->priv->search_custom_entry_set) + return GTK_ENTRY (tree_view->priv->search_entry); + + return NULL; +} + +/** + * gtk_tree_view_set_search_entry: + * @tree_view: A #GtkTreeView + * @entry: the entry the interactive search code of @tree_view should use or %NULL + * + * Sets the entry which the interactive search code will use for this + * @tree_view. This is useful when you want to provide a search entry + * in our interface at all time at a fixed position. Passing %NULL for + * @entry will make the interactive search code use the built-in popup + * entry again. + * + * Since: 2.10 + */ +void +gtk_tree_view_set_search_entry (GtkTreeView *tree_view, + GtkEntry *entry) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + if (entry != NULL) + g_return_if_fail (GTK_IS_ENTRY (entry)); + + if (tree_view->priv->search_custom_entry_set) + { + if (tree_view->priv->search_entry_changed_id) + { + g_signal_handler_disconnect (tree_view->priv->search_entry, + tree_view->priv->search_entry_changed_id); + tree_view->priv->search_entry_changed_id = 0; + } + g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry, + G_CALLBACK (gtk_tree_view_search_key_press_event), + tree_view); + + g_object_unref (tree_view->priv->search_entry); + } + else if (tree_view->priv->search_window) + { + gtk_widget_destroy (tree_view->priv->search_window); + + tree_view->priv->search_window = NULL; + } + + if (entry) + { + tree_view->priv->search_entry = g_object_ref (entry); + tree_view->priv->search_custom_entry_set = TRUE; + + if (tree_view->priv->search_entry_changed_id == 0) + { + tree_view->priv->search_entry_changed_id = + g_signal_connect (tree_view->priv->search_entry, "changed", + G_CALLBACK (gtk_tree_view_search_init), + tree_view); + } + + g_signal_connect (tree_view->priv->search_entry, "key_press_event", + G_CALLBACK (gtk_tree_view_search_key_press_event), + tree_view); + + gtk_tree_view_search_init (tree_view->priv->search_entry, tree_view); + } + else + { + tree_view->priv->search_entry = NULL; + tree_view->priv->search_custom_entry_set = FALSE; + } +} + +/** + * gtk_tree_view_set_search_position_func: + * @tree_view: A #GtkTreeView + * @func: the function to use to position the search dialog + * @data: user data to pass to @func, or %NULL + * @destroy: Destroy notifier for @data, or %NULL + * + * Sets the function to use when positioning the seach dialog. + * + * Since: 2.10 + **/ +void +gtk_tree_view_set_search_position_func (GtkTreeView *tree_view, + GtkTreeViewSearchPositionFunc func, + gpointer user_data, + GDestroyNotify destroy) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (func !=NULL); + + if (tree_view->priv->search_position_destroy) + (* tree_view->priv->search_position_destroy) (tree_view->priv->search_position_user_data); + + tree_view->priv->search_position_func = func; + tree_view->priv->search_position_user_data = user_data; + tree_view->priv->search_position_destroy = destroy; + if (tree_view->priv->search_position_func == NULL) + tree_view->priv->search_position_func = gtk_tree_view_search_position_func; +} + +/** + * gtk_tree_view_get_search_position_func: + * @tree_view: A #GtkTreeView + * + * Returns the positioning function currently in use. + * + * Return value: the currently used function for positioning the search dialog. + * + * Since: 2.10 + */ +GtkTreeViewSearchPositionFunc +gtk_tree_view_get_search_position_func (GtkTreeView *tree_view) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + + return tree_view->priv->search_position_func; +} + + static void gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog, GtkTreeView *tree_view) @@ -12280,7 +12738,8 @@ gtk_tree_view_search_dialog_hide (GtkWidget *search_dialog, static void gtk_tree_view_search_position_func (GtkTreeView *tree_view, - GtkWidget *search_dialog) + GtkWidget *search_dialog, + gpointer user_data) { gint x, y; gint tree_x, tree_y; @@ -12302,14 +12761,14 @@ gtk_tree_view_search_position_func (GtkTreeView *tree_view, &tree_height); gtk_widget_size_request (search_dialog, &requisition); - if (tree_x + tree_width - requisition.width > gdk_screen_get_width (screen)) + if (tree_x + tree_width > gdk_screen_get_width (screen)) x = gdk_screen_get_width (screen) - requisition.width; else if (tree_x + tree_width - requisition.width < 0) x = 0; else x = tree_x + tree_width - requisition.width; - if (tree_y + tree_height > gdk_screen_get_height (screen)) + if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen)) y = gdk_screen_get_height (screen) - requisition.height; else if (tree_y + tree_height < 0) /* isn't really possible ... */ y = 0; @@ -12418,9 +12877,33 @@ gtk_tree_view_search_button_press_event (GtkWidget *widget, gtk_tree_view_search_dialog_hide (widget, tree_view); + if (event->window == tree_view->priv->bin_window) + gtk_tree_view_button_press (GTK_WIDGET (tree_view), event); + return TRUE; } +static gboolean +gtk_tree_view_search_scroll_event (GtkWidget *widget, + GdkEventScroll *event, + GtkTreeView *tree_view) +{ + gboolean retval = FALSE; + + if (event->direction == GDK_SCROLL_UP) + { + gtk_tree_view_search_move (widget, tree_view, TRUE); + retval = TRUE; + } + else if (event->direction == GDK_SCROLL_DOWN) + { + gtk_tree_view_search_move (widget, tree_view, FALSE); + retval = TRUE; + } + + return retval; +} + static gboolean gtk_tree_view_search_key_press_event (GtkWidget *widget, GdkEventKey *event, @@ -12432,8 +12915,11 @@ gtk_tree_view_search_key_press_event (GtkWidget *widget, g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); /* close window and cancel the search */ - if (event->keyval == GDK_Escape || - event->keyval == GDK_Tab) + if (!tree_view->priv->search_custom_entry_set + && (event->keyval == GDK_Escape || + event->keyval == GDK_Tab || + event->keyval == GDK_KP_Tab || + event->keyval == GDK_ISO_Left_Tab)) { gtk_tree_view_search_dialog_hide (widget, tree_view); return TRUE; @@ -12446,6 +12932,13 @@ gtk_tree_view_search_key_press_event (GtkWidget *widget, retval = TRUE; } + if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) + && (event->keyval == GDK_g || event->keyval == GDK_G)) + { + gtk_tree_view_search_move (widget, tree_view, TRUE); + retval = TRUE; + } + /* select next matching iter */ if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down) { @@ -12453,8 +12946,16 @@ gtk_tree_view_search_key_press_event (GtkWidget *widget, retval = TRUE; } + if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK) + && (event->keyval == GDK_g || event->keyval == GDK_G)) + { + gtk_tree_view_search_move (widget, tree_view, FALSE); + retval = TRUE; + } + /* renew the flush timeout */ - if (retval && tree_view->priv->typeselect_flush_timeout) + if (retval && tree_view->priv->typeselect_flush_timeout + && !tree_view->priv->search_custom_entry_set) { g_source_remove (tree_view->priv->typeselect_flush_timeout); tree_view->priv->typeselect_flush_timeout = @@ -12496,7 +12997,8 @@ gtk_tree_view_search_move (GtkWidget *window, /* search */ gtk_tree_selection_unselect_all (selection); - gtk_tree_model_get_iter_first (model, &iter); + if (!gtk_tree_model_get_iter_first (model, &iter)) + return; ret = gtk_tree_view_search_iter (model, selection, &iter, text, &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1))); @@ -12689,7 +13191,6 @@ gtk_tree_view_search_init (GtkWidget *entry, gint len; gint count = 0; const gchar *text; - GtkWidget *window; GtkTreeIter iter; GtkTreeModel *model; GtkTreeSelection *selection; @@ -12697,7 +13198,6 @@ gtk_tree_view_search_init (GtkWidget *entry, g_return_if_fail (GTK_IS_ENTRY (entry)); g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - window = gtk_widget_get_parent (entry); text = gtk_entry_get_text (GTK_ENTRY (entry)); len = strlen (text); model = gtk_tree_view_get_model (tree_view); @@ -12705,7 +13205,8 @@ gtk_tree_view_search_init (GtkWidget *entry, /* search */ gtk_tree_selection_unselect_all (selection); - if (tree_view->priv->typeselect_flush_timeout) + if (tree_view->priv->typeselect_flush_timeout + && tree_view->priv->search_custom_entry_set) { g_source_remove (tree_view->priv->typeselect_flush_timeout); tree_view->priv->typeselect_flush_timeout = @@ -13041,3 +13542,20 @@ gtk_tree_view_grab_notify (GtkWidget *widget, tree_view->priv->pressed_button = -1; } +static void +gtk_tree_view_state_changed (GtkWidget *widget, + GtkStateType previous_state) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (widget); + + if (GTK_WIDGET_REALIZED (widget)) + { + gdk_window_set_background (widget->window, &widget->style->base[widget->state]); + gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]); + } + + gtk_widget_queue_draw (widget); +} + +#define __GTK_TREE_VIEW_C__ +#include "gtkaliasdef.c"